diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd
--- v1.3.35/linux/Documentation/cdrom/aztcd	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/aztcd	Sun Oct 15 20:02:23 1995
@@ -0,0 +1,793 @@
+$Id: README.aztcd,v 1.80 1995/10/11 19:37:49 root Exp root $
+                       Readme-File README.aztcd
+   for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 
+                          CD-ROM  Driver 
+                       Version 1.7 and newer
+                   (for other drives see 6.-8.)
+
+NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE
+      A PROPRIETARY INTERFACE (implemented on a sound card or on a
+      ISA-AT-bus card). 
+      IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE,
+      such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE
+      WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE 
+      USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER !
+      THE STANDARD-KERNEL 1.2.x NOW ALSO SUPPORTS IDE-CDROM-DRIVES, SEE THE
+      HARDDISK (!) SECTION OF make config, WHEN COMPILING A NEW KERNEL!!!
+----------------------------------------------------------------------------
+
+Contents of this file:
+                         1.  NOTE
+                         2.  INSTALLATION
+                         3.  CONFIGURING YOUR KERNEL
+                         4.  RECOMPILING YOUR KERNEL
+                         4.1   AZTCD AS A RUN-TIME LOADABLE MODULE
+                         4.2   CDROM CONNECTED TO A SOUNDCARD
+                         5.  KNOWN PROBLEMS, FUTURE DEVELOPMENTS
+			 5.1   MULTISESSION SUPPORT
+			 5.2   STATUS RECOGNITION
+			 5.3   DOSEMU's CDROM SUPPORT
+                         6.  BUG REPORTS
+                         7.  OTHER DRIVES
+                         8.  IF YOU DON'T SUCCEED ... DEBUGGING  
+                         9.  TECHNICAL HISTORY OF THE DRIVER
+                        10.  ACKNOWLEDGMENTS
+                        11.  PROGRAMMING ADD ONS: CDPLAY.C
+                        APPENDIX: Source code of cdplay.c
+----------------------------------------------------------------------------
+
+1. NOTE 
+This software has been successfully in alpha and beta test for quite a long
+time with AZTECH CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110 
+and has proven to be stable with kernel versions 1.0.9 to 1.2.0. But with
+any software there still may be bugs in it. So if you encounter problems,
+you are invited to help us improve this software. Please send me a detailed
+bug report (see chapter BUG REPORTS). You are also invited in helping us to 
+increase the number of drives, which are supported.
+
+Please read the README-files carefully and always keep a backup copy of your 
+old kernel, in order to reboot if something goes wrong!
+
+
+2. INSTALLATION
+If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz 
+(xx=version number) and not included in the standard Linux kernel, read the 
+file AZTECH.CDROM.README included in that package to install it. The 
+standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'.
+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/block. It uses /dev/aztcd0, 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.
+
+
+3.  CONFIGURING YOUR KERNEL
+If your kernel is already configured for using the AZTECH driver you will
+see the following message while Linux boots:
+    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
+    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>>>
+    Aztech CD-ROM Init: <drive type> detected
+    Aztech CD-ROM Init: End
+If the message looks different and you are sure to have a supported drive,
+it may have a different base address. The Aztech driver does look for the 
+CD-ROM drive at the base address specified in aztcd.h at compile time. This 
+address can be overwritten by boot parameter aztcd=....You should reboot and 
+start Linux with boot parameter aztcd=<base address>, e.g. aztcd=0x320. If 
+you do not know the base address, start your PC with DOS and look at the boot 
+message of your CD-ROM's DOS driver. If that still does not help, use boot
+parameter aztcd=<base address>,0x79 , this tells aztcd to try a little harder.
+
+If the message looks correct, as user 'root' you should be able to mount the 
+drive by
+          mount -t iso9660 -r /dev/aztcd0 /mnt
+and use it as any other filesystem. (If this does not work, check if
+  /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
+      mknod /dev/aztcd0 b 29 0
+      mkdir /mnt                       
+
+If you still get a different message while Linux boots or when you get the 
+message, that the ISO9660-filesystem is not supported by your kernel, when
+you try to mount the CD-ROM drive, you have to recompile your kernel.
+
+If you do *not* have an Aztech/Orchid/Okano/Wearnes drive and want to bypass
+drive detection during Linux boot up, start with boot parameter aztcd=0.
+
+Joe Nardone has compiled a boot disk with the Aztech driver for installing 
+Slackware from CDROM. You can find the disk images at 'sunsite.unc.edu'; 
+see file 'aztech.gz.README' for instructions on how to use it.
+
+
+4. RECOMPILING YOUR KERNEL
+If your kernel is not yet configured for the AZTECH driver and the ISO9660-
+filesystem, you have to recompile your kernel: 
+
+- Edit aztcd.h to set the I/O-address to your I/O-Base address (AZT_BASE_ADDR), 
+  the driver does not use interrupts or DMA, so if you are using an AZTECH
+  CD268, an ORCHID CD-3110 or ORCHID/WEARNES CDD110 that's the only item you 
+  have to set up. If you have a soundcard, read chapter 4.2.
+  Users of other drives should read chapter OTHER DRIVES of this file.
+  You also can configure that address by LILO boot parameter aztcd=... 
+- There are some other points, which may be configured, e.g. auto-eject the
+  CD when umounting a drive, tray locking etc., see aztcd.h for details.
+- Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support' 
+  (if you want aztcd to be part of the kernel). Do not configure it for
+  'Aztech... support', if you want to use aztcd as a run time loadable module. 
+  But in any case you must have the ISO9660-filesystem included in your
+  kernel.
+- Activate the new kernel, normally this is done by running lilo (don't for-
+  get to configure it before and to keep a copy of your old kernel in case
+  something goes wrong!).
+- Reboot
+- If you've included aztcd in your kernel, you now should see during boot 
+  some messages like
+    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
+    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>
+    Aztech CD-ROM Init: <drive type> detected
+    Aztech CD-ROM Init: End
+- If you have not included aztcd in your kernel, but want to load aztcd as a 
+  run time loadable module see 4.1. 
+- If the message looks correct, as user 'root' you should be able to mount 
+  the drive by
+          mount -t iso9660 -r /dev/aztcd0 /mnt
+  and use it as any other filesystem. (If this does not work, check if
+  /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
+      mknod /dev/aztcd0 b 29 0
+      mkdir /mnt                       
+- If this still does not help, see chapters OTHER DRIVES and DEBUGGING.
+
+4.1 AZTCD AS A RUN-TIME LOADABLE MODULE
+If you do not need aztcd permanently, you can also load and remove the driver
+during runtime via insmod and rmmod. To build aztcd as a loadable module you 
+must *not* configure your kernel for AZTECH support. Nevertheless, you may
+run into problems, if the version of your boot kernel is not the same than
+the source kernel version, from which 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. There are
+also some special features which may be configured, e.g. auto-eject a CD
+when unmounting the drive etc; see aztcd.h for details. Then change
+to /usr/src/linux and do a 
+                    make modules  
+	            make modules_install
+After that you can run-time load the driver via
+                    insmod /lib/modules/X.X.X/misc/aztcd.o
+and remove it via   rmmod  aztcd.
+If you did not set the correct base address in aztcd.h, you can also supply the
+base address when loading the driver via
+                    insmod /lib/modules/X.X.X/misc/aztcd.o aztcd=<base address>
+If you do not have the iso9660-filesystem in your boot kernel, you also have
+to load it before you can mount the CDROM:
+                    insmod /lib/modules/X.X.X/fs/isofs.o
+The mount procedure works as described in 4. above.
+(In all commands 'X.X.X' is the current linux kernel version number. For details
+see file README.modules in /usr/src/linux.)
+
+4.2 CDROM CONNECTED TO A SOUNDCARD
+Most soundcards do have a bus interface to the CDROM-drive. In many cases
+this soundcard needs to be configured, before the CDROM can be used. This
+configuration procedure consists of writing some kind of initialization
+data to the soundcard registers. The AZTECH-CDROM driver in the moment does
+only support one type of soundcard (SoundWave32). Users of other soundcards
+should try to boot DOS first and let their DOS drivers initialize the
+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,
+uncomment line '#define AZT_SW32' and set the appropiate value for
+AZT_BASE_ADDR and AZT_SW32_BASE_ADDR. This support was tested with an Orchid
+CDS-3110 connected to a SoundWave32.
+If you want your soundcard to be supported, find out, how it needs to be
+configured and mail me (see 6.) the appropriate information. 
+
+5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
+5.1 MULTISESSION SUPPORT
+Multisession support for CD's still is a myth. I implemented and tested a basic
+support for multisession and XA CDs, but I still have not enough CDs and appli-
+cations to test it rigourously. So if you'd like to help me, please contact me
+(Email address see below). As of version 1.4 and newer you can enable the 
+multisession support in aztcd.h by setting AZT_MULTISESSION to 1. Doing so 
+will cause the ISO9660-filesystem to deal with multisession CDs, ie. redirect 
+requests to the Table of Contents (TOC) information from the last session, 
+which contains the info of all previous sessions etc.. If you do set 
+AZT_MULTISESSION to 0, you can use multisession CDs anyway. In that case the 
+drive's firmware will do automatic redirection. For the ISO9660-filesystem any 
+multisession CD  will then look like a 'normal' single session CD. But never-
+theless the data of all sessions is viewable and accessible. So with practical-
+ly all real world applications you won't notice the difference. But as future
+applications may make use of advanced multisession features, I've started to
+implement the interface for the ISO9660 multisession interface via ioctl
+CDROMMULTISESSION.
+
+5.2 STATUS RECOGNITION
+The drive status recognition does not work correctly in all cases. Changing
+a disk or having the door open, when a drive is already mounted, is detected 
+by the Aztech driver itself, but nevertheless causes multiple read attempts
+by the different layers of the ISO9660-filesystem driver, which finally timeout,
+so you have to wait quite a little... But isn't it bad style to change a disk 
+in a mounted drive, anyhow ?!
+
+The driver uses busy wait in most cases for the drive handshake (macros
+STEN_LOW and DTEN_LOW). I tested with a 486/DX2 at 66MHz and a Pentium at
+60MHz. Whenever you use a much faster machine you are likely to get timeout 
+messages. In that case edit aztcd.h and increase the timeout value AZT_TIMEOUT. 
+
+For some 'slow' drive commands I implemented waiting with a timer waitqueue
+(macro STEN_LOW_WAIT). If you get this timeout message, you may also edit
+aztcd.h and increase the timeout value AZT_STATUS_DELAY. The waitqueue has
+shown to be a little critical. If you get kernel panic messages, edit aztcd.c
+and substitute STEN_LOW_WAIT by STEN_LOW. Busy waiting with STEN_LOW is more
+stable, but also causes CPU overhead.
+
+5.3 DOSEMU's CD-ROM SUPPORT
+With release 1.20 aztcd was modified to allow access to CD-ROMS when running
+under dosemu-0.60. aztcd-versions before 1.20 are most likely to crash
+Linux, when a CD-ROM is accessed under dosemu. This problem has partly been
+fixed, but still when accessing a directory for the first time the system
+might hang for some 30sec. So be patient, when using dosemu's CD-ROM support
+in combination with aztcd :-) ! Unfortunately up to now, I could not locate
+the root cause of that problem. It seems to be somewhere in the interaction
+of the kernel with dosemu's and aztcd's buffers. I appreciate any help on
+this subject ! 
+This problem has now (July 1995) been fixed by a modification to dosemu's
+CD-ROM driver, but it is unclear, with which version of dosemu this modi-
+fication will officially be included.
+
+6. BUG REPORTS
+Please send detailed bug reports and bug fixes via EMail to
+
+        zimmerma@rz.fht-esslingen.de
+
+Please include a description of your CD-ROM drive type and interface card, 
+the exact firmware message during Linux bootup, the version number of the 
+AZTECH-CDROM-driver and the Linux kernel version. Also a description of your 
+system's other hardware could be of interest, especially microprocessor type, 
+clock frequency, other interface cards such as soundcards, ethernet adapter, 
+game cards etc..
+
+I will try to collect the reports and make the necessary modifications from 
+time to time. I may also come back to you directly with some bug fixes and 
+ask you to do further testing and debugging.
+
+Editors of CD-ROMs are invited to send a 'cooperation' copy of their
+CD-ROMs to the volunteers, who provided the CD-ROM support for Linux. My
+snail mail address for such 'stuff' is
+           Prof. Dr. W. Zimmermann
+           Fachhochschule fuer Technik Esslingen
+           Fachbereich NT
+           Flandernstrasse 101
+           D-73732 Esslingen
+           Germany
+
+
+7. OTHER DRIVES
+The following drives ORCHID CDS3110, OKANO CDD110 and WEARNES nearly look 
+the same as AZTECH CDA268-01A, especially they seem to use the same command 
+codes. So it was quite simple to make the AZTECH driver work with these drives. 
+
+Unfortunately I do not have any of these drives available, so I couldn't test
+it myself. But I've got reports, that it works with ORCHID CDS3110 and Game-
+Wave32 sound cards and also with WEARNES CDD110 in some different combinations. 
+In some installations, it seems necessary to initialize the drive with the DOS 
+driver before (especially if combined with a sound card) and then do a warm 
+boot (CTRL-ALT-RESET) or start Linux from DOS, e.g. with 'loadlin'.
+
+If you do not succeed, read chapter DEBUGGING. Thanks in advance!
+
+Sorry for the inconvenience, but it is difficult to develop for hardware, 
+which you don't have available for testing. So if you like, please help us.
+
+
+8. DEBUGGING : IF YOU DON'T SUCCEED, TRY THE FOLLOWING
+-reread the complete README file
+-make sure, that your drive is hardware configured for 
+    transfer mode: polled
+    IRQ:           not used
+    DMA:           not used
+    Base Address:  something like 300, 320 ...
+ You can check this, when you start the DOS driver, which came with your
+ drive. By appropriately configuring the drive and the DOS driver you can
+ check, whether your drive does operate in this mode correctly under DOS. If
+ it does not operate under DOS, it won't under Linux.
+ Make sure the Base Address is configured correctly in aztcd.h, also make
+ sure, that /dev/aztcd0 exists with the correct major number (compare it with
+ the entry in file /usr/include/linux/major.h for the Aztech drive). 
+-insert a CD-ROM and close the tray
+-cold boot your PC (i.e. via the power on switch or the reset button)
+-if you start Linux via DOS, e.g. using loadlin, make sure, that the DOS
+ driver for the CD-ROM drive is not loaded (comment out the calling lines 
+ in DOS' config.sys!)
+-look for the aztcd: init message during Linux init and note them exactly
+-log in as root and do a mount -t iso9660 /dev/aztcd0 /mnt
+-if you don't succeed in the first time, try several times. Try also to open
+ and close the tray, then mount again. Please note carefully all commands
+ you typed in and the aztcd-messages, which you get.
+-if you get an 'Aztech CD-ROM init: aborted' message, read the remarks about
+ the version string below.
+
+If this does not help, do the same with the following differences 
+-start DOS before; make now sure, that the DOS driver for the CD-ROM is 
+ loaded under DOS (i.e. uncomment it again in config.sys)
+-warm boot your PC (i.e. via CTRL-ALT-DEL)
+ if you have it, you can also start via loadlin (try both).
+ ...
+ Again note all commands and the aztcd-messages.
+
+If you see STEN_LOW or STEN_LOW_WAIT error messages, increase the timeout
+values.
+
+If this still does not help, 
+-look in aztcd.c for the lines  #if 0
+                                #define AZT_TEST1
+                                ...
+                                #endif
+ and substitute '#if 0' by '#if 1'.
+-recompile your kernel and repeat the above two procedures. You will now get 
+ a bundle of debugging messages from the driver. Again note your commands
+ and the appropriate messages. If you have syslogd running, these messages
+ may also be found in syslogd's kernel log file. Nevertheless in some
+ installations syslogd does not yet run, when init() is called, thus look for
+ the aztcd-messages during init, before the login-prompt appears.
+ Then look in aztcd.c, to find out, what happened. The normal calling sequence 
+ is: aztcd_init() during Linux bootup procedure init()
+ after doing a 'mount -t iso9660 /dev/aztcd0 /mnt' the normal calling sequence is
+     aztcd_open()    -> Status 2c after cold reboot with CDROM or audio CD inserted
+                     -> Status 8  after warm reboot with CDROM inserted          
+                     -> Status 2e after cold reboot with no disk, closed tray
+                     -> Status 6e after cold reboot, mount with door open
+     aztUpdateToc()
+     aztGetDiskInfo()
+     aztGetQChannelInfo()   repeated several times
+     aztGetToc()
+     aztGetQChannelInfo()   repeated several times
+     a list of track informations
+     do_aztcd_request()  }  
+     azt_transfer()    } repeated several times
+     azt_poll          }
+ Check, if there is a difference in the calling sequence or the status flags!
+ 
+ There are a lot of other messages, eg. the ACMD-command code (defined in
+ aztcd.h), status info from the getAztStatus-command and the state sequence of
+ the finite state machine in azt_poll(). The most important are the status
+ messages, look how they are defined and try to understand, if they make
+ sense in the context where they appear. With a CD-ROM inserted the status 
+ should always be 8, except in aztcd_open(). Try to open the tray, insert a
+ audio disk, insert no disk or reinsert the CD-ROM and check, if the status
+ bits change accordingly. The status bits are the most likely point, where 
+ the drive manufacturers may implement changes.
+            
+If you still don't succeed, a good point to start is to look in aztcd.c in 
+function aztcd_init, where the drive should be detected during init. Do the
+following:
+-reboot the system with boot parameter 'aztcd=<your base address>,0x79'. With
+ parameter 0x79 most of the drive version detection is bypassed. After that 
+ you should see the complete version string including leading and trailing 
+ blanks during init. 
+ Now adapt the statement
+      if ((result[1]=='A')&&(result[2]=='Z' ...)
+ in aztcd_init() to exactly match the first 3 or 4 letters you have seen.
+-Another point is the 'smart' card detection feature in aztcd_init(). Normally
+ the CD-ROM drive is ready, when aztcd_init is trying to read the version
+ string and a time consuming ACMD_SOFT_RESET command can be avoided. This is
+ detected by looking, if AFL_OP_OK can be read correctly. If the CD-ROM drive 
+ hangs in some unknown state, e.g. because of an error before a warm start or 
+ because you first operated under DOS, even the version string may be correct, 
+ but the following commands will not. Then change the code in such a way, 
+ that the ACMD_SOFT_RESET is issued in any case, by substituting the
+ if-statement 'if ( ...=AFL_OP_OK)' by 'if (1)'.
+
+If you succeed, please mail may the exact version string of your drive and
+the code modifications, you have made together with a short explanation.
+If you don't succeed, you may mail me the output of the debugging messages.
+But remember, they are only useful, if they are exact and complete and you
+describe in detail your hardware setup and what you did (cold/warm reboot,
+with/without DOS, DOS-driver started/not started, which Linux-commands etc.)
+
+
+9. TECHNICAL HISTORY OF THE DRIVER
+The AZTECH-Driver is a rework of the Mitsumi-Driver. Four major items had to
+be reworked:
+
+a) The Mitsumi drive does issue complete status information acknowledging
+each command, the Aztech drive does only signal that the command was
+processed. So whenever the complete status information is needed, an extra
+ACMD_GET_STATUS command is issued. The handshake procedure for the drive
+can be found in the functions aztSendCmd(), sendAztCmd() and getAztStatus().
+
+b) The Aztech Drive does not have a ACMD_GET_DISK_INFO command, so the
+necessary info about the number of tracks (firstTrack, lastTrack), disk
+length etc. has to be read from the TOC in the lead in track (see function
+aztGetDiskInfo()).
+
+c) Whenever data is read from the drive, the Mitsumi drive is started with a
+command to read an indefinite (0xffffff) number of sectors. When the appropriate 
+number of sectors is read, the drive is stopped by a ACDM_STOP command. This
+does not work with the Aztech drive. I did not find a way to stop it. The
+stop and pause commands do only work in AUDIO mode but not in DATA mode.
+Therefore I had to modify the 'finite state machine' in function azt_poll to
+only read a certain number of sectors and then start a new read on demand. As I 
+have not completely understood, how the buffer/caching scheme of the Mitsumi 
+driver was implemented, I am not sure, if I have covered all cases correctly, 
+whenever you get timeout messages, the bug is most likely to be in that
+function azt_poll() around switch(cmd) .... case ACD_S_DATA. 
+
+d) I did not get information about changing drive mode. So I doubt, that the
+code around function azt_poll() case AZT_S_MODE does work. In my test I have
+not been able to switch to reading in raw mode. For reading raw mode, Aztech
+uses a different command than for cooked mode, which I only have implemen-
+ted in the ioctl-section but not in the section which is used by the ISO9660- 
+
+The driver was developed on an AST PC with Intel 486/DX2, 8MB RAM, 340MB IDE 
+hard disk and on an AST PC with Intel Pentium 60MHz, 16MB RAM, 520MB IDE 
+running Linux kernel version 1.0.9 from the LST 1.8 Distribution. The kernel 
+was compiled with gcc.2.5.8. My CD-ROM drive is an Aztech CDA268-01A. My
+drive says, that it has Firmware Version AZT26801A1.3. It came with a ISA-bus
+interface card and works with polled I/O without DMA and without interrupts.
+The code for all other drives was 'remote' tested and debugged by a number of 
+volunteers on the Internet.
+
+Points, where I feel that possible problems might be and all points where I 
+did not completely understand the drive's behaviour or trust my own code are 
+marked with /*???*/ in the source code. There are also some parts in the 
+Mitsumi driver, where I did not completely understand their code.
+
+
+10. ACKNOWLEDGMENTS
+Without the help of P.Bush, Aztech, who delivered technical information
+about the Aztech Drive and without the help of E.Moenkeberg, GWDG, who did a
+great job in analyzing the command structure of various CD-ROM drives, this
+work would not have been possible. E.Moenkeberg was also a great help in 
+making the software 'kernel ready' and in answering many of the CDROM-related 
+questions in the newsgroups. He really is *the* Linux CD-ROM guru. Thanks 
+also to all the guys on the Internet, who collected valuable technical 
+information about CDROMs. 
+
+Joe Nardone (joe@access.digex.net) was a patient tester even for my first
+trial, which was more than slow, and made suggestions for code improvement.
+Especially the 'finite state machine' azt_poll() was rewritten by Joe to get
+clean C code and avoid the ugly 'gotos', which I copied from mcd.c.
+
+Robby Schirmer (schirmer@fmi.uni-passau.de) tested the audio stuff (ioctls) 
+and suggested a lot of patches for them.
+
+Joseph Piskor and Peter Nugent were the first users with the ORCHID CD3110
+and also were very patient with the problems which occurred.
+
+Reinhard Max delivered the information for the CDROM-interface of the
+SoundWave32 soundcards.
+
+Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
+directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'.
+
+11. PROGRAMMING ADD ONs: cdplay.c
+You can use the ioctl-functions included in aztcd.c in your own programs. As
+an example on how to do this, you will find a tiny CD Player for audio CDs 
+named 'cdplay.c'. It allows you to play audio CDs. You can play a specified 
+track, pause and resume or skip tracks forward and backwards. If you quit the 
+program without stopping the  drive, playing is continued. You can also 
+(mis)use cdplay to read and hexdump data disks.
+'cdplay.c' can be found as a separate file in package AZTECH.CDROM.Vxx.tgz.
+If you don't have it, you can find the code in the APPENDIX of this file,
+which you should cut out with an editor and store in a separate file
+'cdplay.c'. To compile it and make it executable, do
+  gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # compiles it
+  chmod +755 /usr/local/bin/cdplay                              # makes it executable
+  ln -s /dev/aztcd0 /dev/cdrom                                  # creates a link
+   (for /usr/lib substitute the top level directory, where your include files 
+    reside,  and for /usr/local/bin the directory, where you want the executable 
+    binary to reside )
+
+You have to set the correct permissions for cdplay *and* for /dev/mcd0 or
+/dev/aztcd0 in order to use it. Remember, that you should not have /dev/cdrom 
+mounted, when you're playing audio CDs. 
+
+This program is just a hack for testing the ioctl-functions in aztcd.c, I will 
+not maintain it, so if you run into problems, discard it or have a look into 
+the source code 'cdplay.c'. The program does only contain a minimum of user 
+protection and input error detection. If you use the commands in the wrong 
+order or if you try to read a CD at wrong addresses, you may get error messages
+or even hang your machine. If you get STEN_LOW, STEN_LOW_WAIT or segment violation 
+error messages when using cdplay, after that, the system might not be stable 
+any more, so you'd better reboot. As the ioctl-functions run in kernel mode,
+most normal Linux-multitasking protection features do not work. By using
+uninitialized 'wild' pointers etc., it is easy to write to other users data and
+program areas, destroy kernel tables etc.. So if you experiment with ioctls
+as always when you are doing systems programming and kernel hacking, you
+should have a backup copy of your system in a safe place (and you also
+should try before, how to restore from a backup copy)!
+
+
+Werner Zimmermann
+Fachhochschule fuer Technik Esslingen
+(EMail: zimmerma@rz.fht-esslingen.de)
+July 26, 1995
+
+---------------------------------------------------------------------------
+APPENDIX: Source code of cdplay.c
+
+/* Tiny Audio CD Player
+
+   Copyright 1994, 1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+
+This program originally was written to test the audio functions of the
+AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before 
+using it, you should set a symlink from /dev/cdrom to your real CDROM
+device.
+
+The GNU General Public License applies to this program.
+
+History:  V0.1  W.Zimmermann: First release. Nov. 8, 1994
+          V0.2  W.Zimmermann: Enhanced functionality. Nov. 9, 1994
+          V0.3  W.Zimmermann: Additional functions. Nov. 28, 1994          
+          V0.4  W.Zimmermann: fixed some bugs. Dec. 17, 1994
+          V0.5  W.Zimmermann: clean 'scanf' commands without compiler warnings
+                              Jan. 6, 1995
+          V0.6  W.Zimmermann: volume control (still experimental). Jan. 24, 1995
+          V0.7  W.Zimmermann: read raw modified. July 26, 95
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/cdrom.h>
+#include <linux/aztcd.h>
+
+void help(void)
+{ printf("Available Commands:  STOP         s      EJECT/CLOSE  e       QUIT         q\n");
+  printf("                     PLAY TRACK   t      PAUSE        p       RESUME       r\n");
+  printf("                     NEXT TRACK   n      REPEAT LAST  l       HELP         h\n");
+  printf("                     SUB CHANNEL  c      TRACK INFO   i       PLAY AT      a\n");
+  printf("                     READ         d      READ RAW     w       VOLUME       v\n");
+}
+
+int main(void)
+{ int handle;
+  unsigned char command=' ', ini=0, first=1, last=1;
+  unsigned int cmd, i,j,k, arg1,arg2,arg3;
+  struct cdrom_ti       ti;
+  struct cdrom_tochdr   tocHdr;
+  struct cdrom_subchnl  subchnl;
+  struct cdrom_tocentry entry;
+  struct cdrom_msf      msf;
+  union  { struct cdrom_msf msf;
+           unsigned char buf[CD_FRAMESIZE_RAW];
+         } azt;
+  struct cdrom_volctrl  volctrl;
+
+  printf("\nMini-Audio CD-Player V0.71   (C) 1994,1995  W.Zimmermann\n");
+  handle=open("/dev/cdrom",O_RDWR);
+  ioctl(handle,CDROMRESUME);
+  
+  if (handle<=0) 
+    { printf("Drive Error: already playing, no audio disk, door open\n");
+      printf("             or no permission (you must be ROOT in order to use this program)\n");
+    }
+  else
+    { help();
+      while (1)
+        { printf("Type command (h = help):  ");
+          scanf("%s",&command); 
+          switch (command)
+            { case 'e':   cmd=CDROMEJECT;
+                          ioctl(handle,cmd);
+                          break;  
+              case 'p':   if (!ini)
+                             { printf("Command not allowed - play track first\n");
+                             }
+                          else
+                             { cmd=CDROMPAUSE;
+                               if (ioctl(handle,cmd)) printf("Drive Error\n");
+                             }
+                          break;
+              case 'r':   if (!ini)
+                             { printf("Command not allowed - play track first\n");
+                             }
+                          else
+                             { cmd=CDROMRESUME;
+                               if (ioctl(handle,cmd)) printf("Drive Error\n");
+                             }
+                          break;
+              case 's':   cmd=CDROMPAUSE;
+                          if (ioctl(handle,cmd)) printf("Drive error or already stopped\n");
+                          cmd=CDROMSTOP;
+                          if (ioctl(handle,cmd)) printf("Drive error\n");
+                          break;
+              case 't':   cmd=CDROMREADTOCHDR;
+                          if (ioctl(handle,cmd,&tocHdr)) printf("Drive Error\n");
+                          first=tocHdr.cdth_trk0;
+                          last= tocHdr.cdth_trk1;
+                          if ((first==0)||(first>last))
+                            { printf ("--could not read TOC\n");
+                            }
+                          else
+                            { printf("--first track: %d   --last track: %d   --enter track number: ",first,last);
+                              cmd=CDROMPLAYTRKIND;
+                              scanf("%i",&arg1);
+                              ti.cdti_trk0=arg1;
+                              if (ti.cdti_trk0<first) ti.cdti_trk0=first;
+                              if (ti.cdti_trk0>last)  ti.cdti_trk0=last;
+                              ti.cdti_ind0=0;
+                              ti.cdti_trk1=last;
+                              ti.cdti_ind1=0;
+                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+                              ini=1;
+                            } 
+                          break;
+              case 'n':   if (!ini++) 
+                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
+                              first=tocHdr.cdth_trk0;
+                              last= tocHdr.cdth_trk1;
+                              ti.cdti_trk0=first-1;
+                            }
+                          if ((first==0)||(first>last))
+                            { printf ("--could not read TOC\n");
+                            }
+                          else
+                            { cmd=CDROMPLAYTRKIND;
+                              if (++ti.cdti_trk0 > last)  ti.cdti_trk0=last;
+                              ti.cdti_ind0=0;
+                              ti.cdti_trk1=last;
+                              ti.cdti_ind1=0;
+                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+                              ini=1;
+                            }
+                          break;
+              case 'l':   if (!ini++)
+                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
+                              first=tocHdr.cdth_trk0;
+                              last= tocHdr.cdth_trk1;
+                              ti.cdti_trk0=first+1;
+                            }
+                          if ((first==0)||(first>last))
+                            { printf ("--could not read TOC\n");
+                            }
+                          else
+                            { cmd=CDROMPLAYTRKIND;
+                              if (--ti.cdti_trk0 < first) ti.cdti_trk0=first;
+                              ti.cdti_ind0=0;
+                              ti.cdti_trk1=last;
+                              ti.cdti_ind1=0;
+                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+                              ini=1;
+                            }  
+                          break;
+              case 'c':   subchnl.cdsc_format=CDROM_MSF;
+                          if (ioctl(handle,CDROMSUBCHNL,&subchnl)) 
+                            printf("Drive Error\n");
+                          else
+                            { printf("AudioStatus:%s   Track:%d  Mode:%d   MSF=%d:%d:%d\n", \
+                              subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",\
+                              subchnl.cdsc_trk,subchnl.cdsc_adr, \
+                              subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, \
+                              subchnl.cdsc_absaddr.msf.frame);
+                            }
+                          break;              
+              case 'i':   if (!ini)
+                            { printf("Command not allowed - play track first\n");
+                            }
+                          else
+                            { cmd=CDROMREADTOCENTRY;
+                              printf("Track No.: ");
+                              scanf("%d",&arg1);
+                              entry.cdte_track=arg1;
+                              if (entry.cdte_track<first) entry.cdte_track=first;
+                              if (entry.cdte_track>last)  entry.cdte_track=last;
+			      entry.cdte_format=CDROM_MSF;
+                              if (ioctl(handle,cmd,&entry)) 
+                               { printf("Drive error or invalid track no.\n");
+                               }
+                              else
+                               { printf("Mode %d Track, starts at %d:%d:%d\n", \
+                               entry.cdte_adr,entry.cdte_addr.msf.minute, \
+                               entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame);
+                               }
+                            }
+                          break;
+              case 'a':   cmd=CDROMPLAYMSF;
+                          printf("Address (min:sec:frame)  ");
+                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+                          msf.cdmsf_min0  =arg1;
+                          msf.cdmsf_sec0  =arg2;
+                          msf.cdmsf_frame0=arg3;
+                          if (msf.cdmsf_sec0  > 59) msf.cdmsf_sec0  =59;
+                          if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74;
+                          msf.cdmsf_min1=60;
+                          msf.cdmsf_sec1=00;
+                          msf.cdmsf_frame1=00;
+                          if (ioctl(handle,cmd,&msf)) 
+                           { printf("Drive error or invalid address\n");
+                           }
+                          break;
+#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
+              case 'd':   cmd=CDROMREADCOOKED;
+                          printf("Address (min:sec:frame)  ");
+                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+                          azt.msf.cdmsf_min0  =arg1;
+                          azt.msf.cdmsf_sec0  =arg2;
+                          azt.msf.cdmsf_frame0=arg3;
+                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
+                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
+                          if (ioctl(handle,cmd,&azt.msf)) 
+                           { printf("Drive error, invalid address or unsupported command\n");
+                           }
+                          k=0;
+                          getchar();
+                          for (i=0;i<128;i++)
+                           { printf("%4d:",i*16);
+                             for (j=0;j<16;j++)
+                               { printf("%2x ",azt.buf[i*16+j]);
+                               }
+                             for (j=0;j<16;j++)
+                               { if (isalnum(azt.buf[i*16+j])) 
+                                   printf("%c",azt.buf[i*16+j]);
+                                 else
+                                   printf(".");
+                               }
+                             printf("\n"); 
+                             k++;
+                             if (k>=20)
+                              { printf("press ENTER to continue\n");
+                                getchar();
+                                k=0;
+                              }
+                           } 
+                          break;
+              case 'w':   cmd=CDROMREADRAW;
+                          printf("Address (min:sec:frame)  ");
+                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+                          azt.msf.cdmsf_min0  =arg1;
+                          azt.msf.cdmsf_sec0  =arg2;
+                          azt.msf.cdmsf_frame0=arg3;                          
+                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
+                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
+                          if (ioctl(handle,cmd,&azt)) 
+                           { printf("Drive error, invalid address or unsupported command\n");
+                           }
+                          k=0;
+                          for (i=0;i<147;i++)
+                           { printf("%4d:",i*16);
+                             for (j=0;j<16;j++)
+                               { printf("%2x ",azt.buf[i*16+j]);
+                               }
+                             for (j=0;j<16;j++)
+                               { if (isalnum(azt.buf[i*16+j])) 
+                                   printf("%c",azt.buf[i*16+j]);
+                                 else
+                                   printf(".");
+                               }
+                             printf("\n"); 
+                             k++;
+                             if (k>=20)
+                              { getchar();
+                                k=0;
+                              }
+                           } 
+                          break;
+#endif
+              case 'v':   cmd=CDROMVOLCTRL;
+                          printf("--Channel 0 Left  (0-255): ");
+                          scanf("%d",&arg1);
+                          printf("--Channel 1 Right (0-255): ");
+                          scanf("%d",&arg2);
+                          volctrl.channel0=arg1;
+                          volctrl.channel1=arg2;
+                          volctrl.channel2=0;
+                          volctrl.channel3=0;
+                          if (ioctl(handle,cmd,&volctrl)) 
+                           { printf("Drive error or unsupported command\n");
+                           }
+                          break;  
+              case 'q':   if (close(handle)) printf("Drive Error: CLOSE\n");
+                          exit(0);
+              case 'h':   help();
+                          break;
+              default:    printf("unknown command\n");
+                          break;
+            }
+       }
+    }
+  return 0;
+}
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/cdu31a linux/Documentation/cdrom/cdu31a
--- v1.3.35/linux/Documentation/cdrom/cdu31a	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/cdu31a	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,160 @@
+   Tips for using cdu31a.c, the driver for Sony CDU-31A and CDU-33A CDROM
+   drives.
+  
+   Corey Minyard (minyard@wf-rch.cirr.com)
+  
+   Colossians 3:17
+  
+   The Sony interface device driver handles Sony interface CDROM
+   drives and provides a complete block-level interface as well as an
+   ioctl() interface compatible with the Sun (as specified in
+   include/linux/cdrom.h).  With this interface, CDROMs can be
+   accessed and standard audio CDs can be played back normally.
+  
+   WARNING - 	All autoprobes have been removed from the driver.
+  		You MUST configure the CDU31A via a LILO config
+  		at boot time or in lilo.conf.  I have the
+  		following in my lilo.conf:
+  
+                  append="cdu31a=0x1f88,0,PAS"
+  
+  		The first number is the I/O base address of the
+  		card.  The second is the interrupt (0 means none).
+  		The third should be "PAS" if on a Pro-Audio
+  		spectrum, or nothing if on something else.
+  
+   This interface is (unfortunately) a polled interface.  This is
+   because most Sony interfaces are set up with DMA and interrupts
+   disables.  Some (like mine) do not even have the capability to
+   handle interrupts or DMA.  For this reason you will see a lot of
+   the following:
+  
+     retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+     while ((retry_count > jiffies) && (! <some condition to wait for))
+     {
+        while (handle_sony_cd_attention())
+           ;
+  
+        sony_sleep();
+     }
+     if (the condition not met)
+     {
+        return an error;
+     }
+  
+   This ugly hack waits for something to happen, sleeping a little
+   between every try.  it also handles attentions, which are
+   asynchronous events from the drive informing the driver that a disk
+   has been inserted, removed, etc.
+  
+   NEWS FLASH - The driver now supports interrupts but they are
+   turned off by default.  Use of interrupts is highly encouraged, it
+   cuts CPU usage down to a reasonable level.  I had DMA in for a while
+   but PC DMA is just too slow.  Better to just insb() it.
+  
+   One thing about these drives: They talk in MSF (Minute Second Frame) format.
+   There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+   disk.  The funny thing is that these are sent to the drive in BCD, but the
+   interface wants to see them in decimal.  A lot of conversion goes on.
+  
+   DRIVER SPECIAL FEATURES
+   -----------------------
+  
+   This section describes features beyond the normal audio and CD-ROM
+   functions of the drive.
+  
+   2048 byte buffer mode
+  
+   If a disk is mounted with -o block=2048, data is copied straight
+   from the drive data port to the buffer.  Otherwise, the readahead
+   buffer must be involved to hold the other 1K of data when a 1K
+   block operation is done.  Note that with 2048 byte blocks you
+   cannot execute files from the CD.
+  
+   XA compatibility
+  
+   The driver should support XA disks for both the CDU31A and CDU33A.
+   It does this transparently, the using program doesn't need to set it.
+  
+   Multi-Session
+  
+   A multi-session disk looks just like a normal disk to the user.
+   Just mount one normally, and all the data should be there.
+   A special thanks to Koen for help with this!
+   
+   Raw sector I/O
+  
+   Using the CDROMREADAUDIO it is possible to read raw audio and data
+   tracks.  Both operations return 2352 bytes per sector.  On the data
+   tracks, the first 12 bytes is not returned by the drive and the value
+   of that data is indeterminate.
+  
+  
+    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.
+  
+  /
+
+/*
+  
+   Setting up the Sony CDU31A/CDU33A drive interface card.  If
+   You have another card, you are on your own.
+   
+        +----------+-----------------+----------------------+
+        |  JP1     |  34 Pin Conn    |                      |
+        |  JP2     +-----------------+                      |
+        |  JP3                                              |
+        |  JP4                                              |
+        |                                                   +--+
+        |                                                   |  +-+
+        |                                                   |  | |  External
+        |                                                   |  | |  Connector
+        |                                                   |  | |
+        |                                                   |  +-+
+        |                                                   +--+
+        |                                                   |
+        |                                          +--------+
+        |                                          |
+        +------------------------------------------+
+   
+      JP1 sets the Base Address, using the following settings:
+   
+        Address         Pin 1           Pin 2
+        -------         -----           -----
+        0x320           Short           Short
+        0x330           Short           Open
+        0x340           Open            Short
+        0x360           Open            Open
+   
+      JP2 and JP3 configure the DMA channel; they must be set the same.
+   
+        DMA             Pin 1           Pin 2           Pin 3
+        ---             -----           -----           -----
+        1               On              Off             On
+        2               Off             On              Off
+        3               Off             Off             On
+   
+      JP4 Configures the IRQ:
+   
+        IRQ     Pin 1           Pin 2           Pin 3           Pin 4
+        ---     -----           -----           -----           -----
+        3       Off             Off             On              Off
+        4       Off             Off*            Off             On
+        5       On              Off             Off             Off
+        6       Off             On              Off             Off
+   
+                  The documentation states to set this for interrupt
+                  4, but I think that is a mistake.
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/cm206 linux/Documentation/cdrom/cm206
--- v1.3.35/linux/Documentation/cdrom/cm206	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/cm206	Wed Jul 19 10:15:42 1995
@@ -0,0 +1,177 @@
+This is the readme file for the driver for the Philips/LMS cdrom drive
+cm206 in combination with the cm260 host adapter card. 
+
+				(c) 1995 David A. van Leeuwen
+   
+Features as of version 0.33
+---------------------------
+- Full audio support, that is, both  workman, workbone and cdp work
+  now reasonably. Reading TOC still takes some time. xmcd has been
+  reported to run successfully. 
+- Made auto-probe code a little better, i hope
+
+Features as of version 0.28
+---------------------------
+- Full speed stransfer rate (300 kB/s).
+- Minimum kernel memory usage for buffering (less than 3 kB).
+- Multisession support.
+- Tray locking.
+- Statistcics of driver accessible to the user.
+- Module support.
+- Auto-probing of adapter card's base port and irq line,
+  also configurable at boot time or module load time.
+
+Features still lacking
+----------------------
+- cm205ms+cm250 support. (I do have cm205ms docs now. I still have to
+  study Kai Petzke's cm205 drives to understand the workings of the
+  cm250 adapter card. Don't bet on me, write a driver yourself!)
+
+
+Decide how you are going to use the driver. There are two
+options:
+
+   (a) installing the driver as a resident part of the kernel
+   (b) compiling the driver as a loadable module
+
+   Further, you must decide if you are going to specify the base port
+   address and the interrupt request line of the adapter card cm260 as
+   boot options for (a), module parameters for (b), use automatic
+   probing of these values, or hard-wire your adaptor cards settings
+   into the source code. If you don't care, you can choose for
+   autoprobing, which is the default. In that case you can move on to
+   the next step.
+
+Compiling the kernel
+--------------------
+1) move to /usr/src/linux and do a 
+
+	make config
+
+   If you have chosen for option (a), answer yes to CONFIG_CM206 and
+   CONFIG_ISO9660_FS.
+
+   If you have chosen for option (b), answer yes to CONFIG_MODVERSIONS
+   and no (!) to CONFIG_CM206 and CONFIG_ISO9660_FS. 
+
+2) then do a 
+	
+	make dep; make clean; make zImage; make modules
+
+3) do the usual things to install a new image (backup the old one, run
+   `rdev -R zImage 1', copy the new image in place, run lilo).  Might
+   be `make zlilo'.
+
+Using the driver as a module
+----------------------------
+If you will only seldomly use the cd-rom driver, you can choose for
+option (b), install as a loadable module. You may have to re-compile
+the module when you upgrade the kernel to a new version. Read the file
+`README.modules' in /usr/src/linux. To install the module, you use the 
+command, as root
+
+	insmod /usr/src/linux/modules/cm206.o
+
+You can specify the base address on the command line as well as the irq 
+line to be used, e.g.
+
+	insmod /usr/src/linux/modules/cm206.o cm206=0x300,11
+
+The order of base port and irq line don't matter; you may specify only
+one, the other will have the value of the compiled-in default.  You
+may also have to install the file-system module `iso9660.o', if you
+didn't compile that into the kernel. (If you use `tcsh' as shell, you
+might consider defining
+
+	alias listinstalledmodules 'cat /proc/modules | awk \{print\$1\}'
+	complete rmmod		'p/1/`listinstalledmodules`/'
+	alias listcompiledmodules '(cd /usr/src/linux/modules; \ls -o *.o)'
+	alias insmod		'insmod /usr/src/linux/modules/\!:1 \!:2*'
+	complete insmod		'p/1/`listcompiledmodules`/'
+
+which makes typing insmod and rmmod somewhat easier.)
+
+Using the driver as part of the kernel
+--------------------------------------
+If you have chosen for option a, you can specify the base-port
+address and irq on the lilo boot command line, e.g.:
+
+	LILO: linux cm206=0x340,11
+
+This assumes that your linux kernel image keyword is `linux'. 
+If you may specify either IRQ (3--11) or base port (0x300--0x370),
+auto probing is turned off for both settings, thus setting the 
+other value to the compiled-in default.
+
+If module parameters and LILO config options don't work
+-------------------------------------------------------
+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
+the defines of CM206_IRQ and CM206_BASE.
+
+
+
+Mounting the cdrom
+------------------
+1) Make sure that there is the right device installed in /dev.
+
+  	mknod /dev/cm206cd b 32 0
+
+2) Make sure there is a mount point, e.g., /cdrom 
+
+	mkdir /cdrom
+
+3) mount using a command like this (run as root):
+
+	mount -rt iso9660 /dev/cm206cd /cdrom
+
+4) For user-mounts, add a line in /etc/fstab
+
+	/dev/cm206cd      /cdrom     iso9660 	ro,noauto,user
+
+   This will allow users to give the commands
+
+	mount /cdrom
+	umount /cdrom
+
+If things don't work
+--------------------
+
+- Try to do a `tail /var/adm/messages' to find out if the driver
+  said anything about what is going wrong during the initialization.
+
+- Try to do a `dd if=/dev/cm206cd | od -tc | less' to read from the
+  CD.
+
+- Look in the /proc directory to see if `cm206' shows up under
+  one of `interrupts', `ioports', `devices' or `modules' (if
+  applicable). 
+
+
+DISCLAIMER 
+---------- 
+I cannot guarantee that this driver works, or that the hardware will
+not be harmed, although i consider it most unlikely. 
+
+I hope that you'll find this driver in some way useful. 
+
+					David van Leeuwen
+					david@tm.tno.nl
+
+Note for Linux CDROM vendors
+-----------------------------
+You are encouraged to include this driver on your Linux CDROM. If
+you do, you might consider sending me a free copy of that cd-rom.
+You can contact me through my e-mail address, david@tm.tno.nl. 
+If this driver is compiled into a kernel to boot off a cdrom, 
+you should actually send me a free copy of that cd-rom. 
+
+Copyright
+---------
+The copyright of the cm206 driver for Linux is 
+
+    (c) 1995 David A. van Leeuwen
+
+The driver is released, like most Linux software, under the conditions 
+of the GNU general public license.
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/gscd linux/Documentation/cdrom/gscd
--- v1.3.35/linux/Documentation/cdrom/gscd	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/gscd	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,60 @@
+	Goldstar R420 CD-Rom device driver README
+
+For all kind of other information about the GoldStar R420 CDROM
+and this Linux device driver is a WWW-URL Page installed:
+
+        http://linux.rz.fh-hannover.de/~raupach        
+
+
+      If you are the editor of a Linux CD, you should
+      enable gscd.c within your boot floppy kernel. Please,
+      send me one of your CDs for free.
+
+
+This current driver version 0.4a only supports reading data from the disk.
+Currently we have no audio and no multisession or XA support.
+The polling interface is used, no DMA.
+
+
+Sometimes the GoldStar R420 is sold in a 'Reveal Multimedia Kit'. This kit's
+drive interface is compatible, too.
+
+
+Installation
+------------
+
+Change to '/usr/src/linux/include/linux' 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. 
+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             
+
+Then go back to '/usr/src/linux/' and 'make config' to build the new
+configuration for your kernel. If you want to use the GoldStar driver
+like a module, don't select 'GoldStar CDROM support'. By the way, you
+have to include the iso9660 filesystem.
+
+Now start compiling the kernel with 'make dep ; make zImage'.
+If you want to use the driver as a module, you have to do 'make modules' 
+and 'make modules_install', additionally.
+Install your new kernel as usual - maybe you do it with 'make zlilo'.
+
+Before you can use the driver, you have to
+   mknod /dev/gscd0 b 16 0
+to create the appropriate device file (once for all times).
+
+If you use modules, you can try to insert the driver.
+Say: 'insmod /usr/src/linux/modules/gscd.o'
+or:  'insmod /usr/src/linux/modules/gscd.o gscd=<address>'
+The driver should report his results now.
+
+That's it! Mount a disk, i.e. 'mount -rt iso9660 /dev/gscd0 /cdrom'
+
+Feel free to report errors and suggestions to the following address.
+Be sure, I'm very happy to receive your comments!
+ 
+        Oliver Raupach                                Hannover, Juni 1995
+(raupach@nwfs1.rz.fh-hannover.de)
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/mcd linux/Documentation/cdrom/mcd
--- v1.3.35/linux/Documentation/cdrom/mcd	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/mcd	Tue Aug 29 07:01:32 1995
@@ -0,0 +1,4 @@
+This driver does not support XA or MultiSession CDs (PhotoCDs). Use the
+experimental driver mcdx.c for that.
+
+You can use mcd for one interface, and mcdx for another.
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/mcdx linux/Documentation/cdrom/mcdx
--- v1.3.35/linux/Documentation/cdrom/mcdx	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/mcdx	Tue Aug 29 07:01:32 1995
@@ -0,0 +1,44 @@
+This is a first attempt to create an `improved' driver for the Mitsumi drives.
+It is able to "live together" with mcd.c, if you have at least two Mitsumi
+drives: each driver can use his own drive.
+
+To allow this "coexistence" as long as mcdx.c is not a superset of mcd.c,
+this driver has to use its own device files. We use MAJOR 20 for it. So,
+you have to do
+
+ # mknod /dev/mcdx0 b 20 0
+ # mknod /dev/mcdx1 b 20 1
+
+and so on, one entry for each drive to support, once.
+
+If you are using the driver as a module, you can specify your ports and IRQs
+like
+
+ # insmod mcdx.o mcdx=0x300,11,0x304,5
+
+and so on ("address,IRQ" pairs).
+This will override the configuration in mcdx.h.
+
+This driver:
+
+	o	handles XA (and hopefully) multi session CDs as well as
+		ordinary CDs;
+	o	supports up to 5 drives (of course, you'll need free 
+		IRQs, i/o ports and slots);
+	o	uses much less kernel memory than the standard mcd driver
+		(no extra driver internal buffers!).
+    o   plays audio (like the `old' driver, I hope)
+
+This version doesn't support yet:
+
+	o	shared IRQs (but it seems to be possible - I've successfully
+                connected two drives to the same irq.  So it's `only' a 
+                problem of the driver.)
+
+This driver never will:
+
+	o	Read digital audio (i.e. copy directly), due to missing
+		hardware features. 
+
+
+heiko@lotte.sax.de
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/optcd linux/Documentation/cdrom/optcd
--- v1.3.35/linux/Documentation/cdrom/optcd	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/optcd	Mon Aug 28 09:58:20 1995
@@ -0,0 +1,46 @@
+This is the README file for the Optics Storage 8000 AT CDROM device driver.
+
+The driver contains code to enable an ISP16 interface if it finds one.  It
+didn't originally (although this README erroneously said so), because I think
+this kind of code should go into its own module. But having to use a hack all
+the time in order to use a part of the standard kernel started to annoy me, so
+I copied the ISP16 code by Eric van der Maarel (maarel@marin.nl) from Vadim
+Model's Sanyo sjcd driver. I'll remove it again from this driver when we have
+some common way to talk to ISP16 interfaces.
+
+My original configuration code for the ISP-16 card can get found at
+      dutette.et.tudelft.nl:/pub/linux/
+and at Eberhard's mirror
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
+
+Much more elaborate information can be found at ftp:rbrf.msk.su,
+where Vadim Model (vadim@rbrf.msk.su) has made available an ISP16
+device driver.
+Vadim's directory is
+      rbrf.msk.su:/linux/mediamagic/
+and Eberhard is holding a mirror at
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
+
+
+Before you can use the driver, you have to create the device file once:
+ # mknod /dev/optcd0 b 17 0
+
+To specify the base address if the driver is "compiled-in" to your kernel,
+you can use the kernel command line item (LILO option)
+             optcd=0x340
+with the right address.
+
+If you have compiled optcd as a module, you can load it with
+ # insmod /usr/src/linux/modules/optcd.o
+or
+ # insmod /usr/src/linux/modules/optcd.o optcd=0x340
+with the matching address value of your interface card.
+
+I have tried the module with several 1.2.x kernel versions, and it seems to
+work, as far as I tested. It also seems to work for several 1.3.x versions.
+If you use it, I'd appreciate success/failure reports. If you find a bug,
+try recompiling the driver with some strategically chosen #undef DEBUG_...'s
+changed into #defines (you'll find them in .../include/linux/optcd.h) and
+include the messages generated in your bug report. Good luck.
+
+Leo Spiekman (spiekman@dutette.et.tudelft.nl)
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/sbpcd linux/Documentation/cdrom/sbpcd
--- v1.3.35/linux/Documentation/cdrom/sbpcd	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/sbpcd	Thu Sep 14 09:46:22 1995
@@ -0,0 +1,1049 @@
+This README belongs to release 3.9 or newer of the SoundBlaster Pro
+(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC)
+CD-ROM driver for Linux.
+
+Sbpcd really, really is NOT for ANY IDE/ATAPI drive!
+Not even if you have an "original" SoundBlaster card with an IDE interface!
+So, you better have a look into README.ide if your port address is 0x1F0,
+0x170, 0x1E8, 0x168 or similar.
+I get tons of mails from IDE/ATAPI drive users - I really can't continue
+any more to answer them all. So, if your drive/interface information sheets
+mention "IDE" (primary, secondary, tertiary, quaternary) and the DOS driver
+invoking line within your CONFIG.SYS is using an address below 0x230:
+DON'T ROB MY LAST NERVE - jumper your interface to address 0x170 and IRQ 15
+(that is the "secondary IDE" configuration), set your drive to "master" and
+use ide-cd as your driver. If you do not have a second IDE hard disk, use the
+LILO commands
+   hdb=noprobe hdc=cdrom
+and get lucky.
+To make it fully clear to you: if you mail me about IDE/ATAPI drive problems,
+my answer is above, and I simply will discard your mail, hoping to stop the
+flood and to find time to lead my 12-years old son towards happy computing.
+
+The driver is able to drive the whole family of "traditional" AT-style (that
+is NOT the new "Enhanced IDE" or "ATAPI" drive standard) Matsushita,
+Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The
+well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
+CR-574 is an IDE/ATAPI drive.
+
+The Longshine LCS-7260 is a double-speed drive which uses the "old"
+Matsushita command set. It is supported - with help by Serge Robyns.
+
+There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563
+with a special controller board. This drive is supported (the interface is
+of the "LaserMate" type), and it is possibly the best buy today (cheaper than
+an internal drive, and you can use it as an internal, too - f.e. plug it into
+a soundcard).
+
+CreativeLabs has a new drive "CD200" and a similar drive "CD200F". The latter
+is made by Funai and sometimes named "E2550UA". Support is under construction
+ - CD200F should work, CD200 is still giving problems.
+Drive detection and playing audio should work. I need qualified feedback
+about the bugs within the data functions or a drive (I never saw a CD200).
+
+The quad-speed TEAC CD-55A drive is supported, but still does not reach "full
+speed". The data rate already reaches 500 kB/sec if you set SBP_BUFFER_FRAMES
+to 64 (it is not recommended to do that for normal "file access" usage, but it
+can speed up things a lot if you use something like "dd" to read from the
+drive; I use it for verifying self-written CDs this way).
+The drive itself is able to deliver 600 kB/sec, so this has to get a point of
+work; with the normal setup, the performance currently is not even as good as
+double-speed.
+
+This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives,
+and again: this driver is in no way usable for any IDE/ATAPI drive. If you 
+think your drive should work and it doesn't: send me the DOS driver for your
+beast (gzipped + uuencoded) and your CONFIG.SYS if you want to ask me for help,
+and include an original log message excerpt, and try to give all information
+a complete idiot needs to understand your hassle already with your first
+mail. And if you want to say "as I have mailed you before", be sure that I
+don't remember your "case" by such remarks; at the moment, I have some 
+hundreds open correspondences about Linux CDROM questions (hope to reduce if
+the IDE/ATAPI user questions disappear). 
+
+
+This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
+SoundFX, Mozart, ...) and with the "no-sound" cards (Panasonic CI-101P,
+LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...).
+
+It finally works now with the "configurable" interface "Sequoia S-1000", too,
+which is found on the Spea Media FX and Ensonic Soundscape sound cards. You
+have to specify the type "SBPRO 2" and the true CDROM port address with it,
+not the "configuration port" address.
+
+If you have a sound card which needs a "configuration driver" instead of
+jumpers for interface types and addresses (like Mozart cards) - those
+drivers get invoked before the DOS CDROM driver in your CONFIG.SYS, typical
+names are "cdsetup.sys" and "mztinit.sys" -, let the sound driver do the
+CDROM port configuration (the leading comments within 
+linux/drivers/sound/mad16.c are just for you!). Hannu Savolainen's mad16.c
+code is able to set up my Mozart card - I simply had to add
+   #define MAD16_CONF 0x06
+   #define MAD16_CDSEL 0x03
+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, 
+because the register layout is different between the "SoundBlaster" and the
+"LaserMate" type.
+
+I got a report that the TEAC interface card "I/F E117098" is of type
+"SoundBlaster" (i.e. you have to set SBPRO to 1) even with the addresses
+0x300 and above. This is unusual, and it can't get covered by the auto
+probing scheme.
+If auto-probing found the drive, the address is correct. The reported type
+may be wrong. A "mount" will give success only if the interface type is set
+right. Playing audio should work with a wrong set interface type, too.
+
+With some TEAC and some CD200 drives I have seen interface cards which seem
+to lack the "drive select" lines; always drive 0 gets addressed. To avoid
+"mirror drives" (four drives detected where you only have one) with such
+interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0 (if
+possible).
+
+
+Up to 4 drives per interface card, and up to 4 interface cards are supported.
+All supported drive families can be mixed, but the CR-521 drives are 
+hard-wired to drive ID 0. The drives have to use different drive IDs, and each
+drive has to get a unique minor number (0...3), corresponding indirectly to 
+its drive ID.
+The drive IDs may be selected freely from 0 to 3 - they do not have to be in
+consecutive order.
+
+As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible
+to change old drives to any ID, too. He writes in this sense:
+   "In order to be able to use more than one single speed drive
+   (they do not have the ID jumpers) you must add a DIP switch
+   and two resistors. The pads are already on the board next to
+   the power connector. You will see the silkscreen for the
+   switch if you remove the top cover.
+                    1 2 3 4
+             ID 0 = x F F x             O = "on"
+             ID 1 = x O F x             F = "off"
+             ID 2 = x F O x             x = "don't care"
+             ID 3 = x O O x
+   Next to the switch are the positions for R76 (7k) and R78
+   (12k). I had to play around with the resistor values - ID 3
+   did not work with other values. If the values are not good,
+   ID 3 behaves like ID 0."
+
+To use more than 4 drives, you simply need a second controller card at a 
+different address and a second cable.
+
+The driver supports reading of data from the CD and playing of audio tracks.
+The audio part should run with WorkMan, xcdplayer, with the "non-X11" products
+CDplayer and WorkBone - tell me if it is not compatible with other software.
+
+With the CR-562 and CR-563 drives, the reading of audio frames is possible.
+This is implemented by an IOCTL function which reads READ_AUDIO frames of
+2352 bytes at once (configurable with the "READ_AUDIO" define, default is 0).
+Reading the same frame a second time gives different data; the frame data 
+start at a different position, but all read bytes are valid, and we always
+read 98 consecutive chunks (of 24 Bytes) as a frame. Reading more than 1 frame
+at once possibly misses some chunks at each frame boundary. This lack has to
+get corrected by external, "higher level" software which reads the same frame 
+again and tries to find and eliminate overlapping chunks (24-byte-pieces).
+
+The transfer rate with reading audio (1-frame-pieces) currently is very slow.
+This can be better reading bigger chunks, but the "missing" chunks possibly
+occur at the beginning of each single frame.
+The software interface possibly may change a bit the day the SCSI driver
+supports it too.
+
+With all but the CR-52x drives, MultiSession is supported.
+Photo CDs work (the "old" drives like CR-521 can access only the first
+session of a photoCD).
+At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to
+convert photo CD image files and Gerd Knorr's viewing utility.
+
+The transfer rate will reach 150 kB/sec with CR-52x drives, 300 kB/sec with
+CR-56x drives, and currently not more than 500 kB/sec (usually less than
+250 kB/sec) with the TEAC quad speed drives.
+XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
+
+This release consists of
+- this README file
+- the driver file linux/drivers/block/sbpcd.c
+- the stub files linux/drivers/block/sbpcd[234].c
+- the header file linux/include/linux/sbpcd.h.
+
+
+To install:
+-----------
+
+1. Setup your hardware parameters. Though the driver does "auto-probing" at a
+   lot of (not all possible!) addresses, this step is recommended for
+   every-day use. You should let sbpcd auto-probe once and use the reported
+   address if a drive got found. The reported type may be incorrect; it is
+   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
+      hardware (near the beginning):
+      a1. Set it up for the appropriate type of interface board.
+          "Original" CreativeLabs sound cards need "SBPRO 1".
+          Most "compatible" sound cards (almost all "non-CreativeLabs" cards)
+          need "SBPRO 0".
+          The "no-sound" board from OmniCd needs the "SBPRO 1" setup.
+          All other "no-sound" boards need the "SBPRO 0" setup.
+          Possibly some TEAC "no-sound" boards need the "SBPRO 1" setup.
+          The Spea Media FX sound card needs "SBPRO 2".
+          sbpcd.c holds some examples in its auto-probe list.
+          If you configure "SBPRO" wrong, the playing of audio CDs will work,
+          but you will not be able to mount a data CD.
+      a2. Tell the address of your CDROM_PORT (not of the sound port).
+      a3. If 4 drives get found, but you have only one, set MAX_DRIVES to 1.
+      a4. Set DISTRIBUTION to 0.
+   b. Additionally for 2.a1 and 2.a2, the setup may be done during
+      boot time (via the "kernel command line" or "LILO option"):
+          sbpcd=0x230,SoundBlaster
+      or
+          sbpcd=0x320,LaserMate
+      or
+          sbpcd=0x330,SPEA
+      This is especially useful if you install a fresh distribution.
+      If the second parameter is a number, it gets taken as the type
+      setting; 0 is "LaserMate", 1 is "SoundBlaster".
+      So, for example
+          sbpcd=0x230,1
+      is equivalent to
+          sbpcd=0x230,SoundBlaster
+
+2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita
+   CD-ROM support and for ISO9660 FileSystem support. If you do not have a
+   second, third, or fourth controller installed, do not say "y" to the 
+   secondary Matsushita CD-ROM questions.
+
+3. Then do a "make dep", then make the kernel image ("make zlilo" or else).
+
+4. Make the device file(s). This step usually already has been done by the
+   MAKEDEV script.
+   The driver uses MAJOR 25, so, if necessary, do
+        mknod /dev/sbpcd  b 25 0       (if you have only one drive)
+   and/or
+        mknod /dev/sbpcd0 b 25 0
+        mknod /dev/sbpcd1 b 25 1
+        mknod /dev/sbpcd2 b 25 2
+        mknod /dev/sbpcd3 b 25 3
+   to make the node(s).
+
+   The "first found" drive gets MINOR 0 (regardless to its jumpered ID), the
+   "next found" (at the same cable) gets MINOR 1, ...
+   
+   For a second interface board, you have to make nodes like
+        mknod /dev/sbpcd4 b 26 0
+        mknod /dev/sbpcd5 b 26 1
+   and so on. Use the MAJORs 26, 27, 28.
+
+   If you further make a link like
+        ln -s sbpcd /dev/cdrom
+   you can use the name /dev/cdrom, too.
+
+5. Reboot with the new kernel.
+
+You should now be able to do
+              mkdir /CD
+and 
+              mount -rt iso9660 /dev/sbpcd /CD
+or
+              mount -rt iso9660 -o block=2048 /dev/sbpcd /CD
+and see the contents of your CD in the /CD directory.
+To use audio CDs, a mounting is not recommended (and it would fail if the
+first track is not a data track).
+
+
+Using sbpcd as a "loadable module":
+-----------------------------------
+
+If you do NOT select "Matsushita/Panasonic CDROM driver support" during the
+"make config" of your kernel, you can build the "loadable module" sbpcd.o.
+Read /usr/src/linux/README.modules on this.
+
+If sbpcd gets used as a module, the support of more than one interface
+card (i.e. drives 4...15) is disabled.
+
+You can specify interface address and type with the "insmod" command like:
+ # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x340,0
+or
+ # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1
+where the last number represents the SBPRO setting (no strings allowed here).
+
+
+Things of interest:
+-------------------
+
+The driver is configured to try the LaserMate type of interface at I/O port
+0x0340 first. If this is not appropriate, sbpcd.h should get changed
+(you will find the right place - just at the beginning).
+
+No DMA and no IRQ is used.
+
+To reduce or increase the amount of kernel messages, edit sbpcd.c and play
+with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug").
+Don't forget to reflect what you do; enabling all DBG_xxx switches at once
+may crash your system.
+
+The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to
+specify "block=2048" as a mount option. Doing this will disable the direct
+execution of a binary from the CD; you have to copy it to a device with the
+standard BLOCK_SIZE (1024) before. So, do not use this if your system is
+directly "running from the CDROM" (like some of YGGDRASIL's installation
+variants). There are CDs on the market (like the german "unifix" Linux
+distribution) which MUST get handled with a block_size of 1024. Generally,
+one can say all the CDs which hold files of the name YMTRANS.TBL are defective;
+do not use block=2048 with those.
+
+Within sbpcd.h, you will find some "#define"s (f.e. EJECT and JUKEBOX). With
+that, you can configure the driver for some special things.
+You can use the appended program "cdtester" to set the auto-eject feature
+during runtime. Jeff Tranter's "eject" utility can do this, too (and more)
+for you.
+
+There is an ioctl CDROMMULTISESSION to obtain with a user program if
+the CD is an XA disk and - if it is - where the last session starts. The
+"cdtester" program illustrates how to call it.
+
+
+Auto-probing at boot time:
+--------------------------
+
+The driver does auto-probing at many well-known interface card addresses,
+but not all:
+Some probings can cause a hang if an NE2000 ethernet card gets touched, because
+SBPCD's auto-probing happens before the initialization of the net drivers.
+Those "hazardous" addresses are excluded from auto-probing; the "kernel 
+command line" feature has to be used during installation if you have your 
+drive at those addresses. The "module" version is allowed to probe at those
+addresses, too.
+
+The auto-probing looks first at the configured address resp. the address
+submitted by the kernel command line. With this, it is possible to use this
+driver within installation boot floppies, and for any non-standard address,
+too.
+
+Auto-probing will make an assumption about the interface type ("SBPRO" or not),
+based upon the address. That assumption may be wrong (initialization will be
+o.k., but you will get I/O errors during mount). In that case, use the "kernel
+command line" feature and specify address & type at boot time to find out the
+right setup.
+
+For every-day use, address and type should get configured within sbpcd.h. That
+will stop the auto-probing due to success with the first try.
+
+The kernel command "sbpcd=0" suppresses each auto-probing and causes
+the driver not to find any drive; it is meant for people who love sbpcd
+so much that they do not want to miss it, even if they miss the drives. ;-)  
+
+If you configure "#define CDROM_PORT 0" in sbpcd.h, the auto-probing is
+initially disabled and needs an explicit kernel command to get activated.
+Once activated, it does not stop before success or end-of-list. This may be
+useful within "universal" CDROM installation boot floppies (but using the 
+loadable module would be better because it allows an "extended" auto-probing
+without fearing NE2000 cards).
+
+To shorten the auto-probing list to a single entry, set DISTRIBUTION 0 within
+sbpcd.h.
+
+
+Setting up address and interface type:
+--------------------------------------
+
+If your I/O port address is not 0x340, you have to look for the #defines near
+the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and
+change CDROM_PORT to the address of your CDROM I/O port.
+
+Almost all of the "SoundBlaster compatible" cards behave like the no-sound
+interfaces, i.e. need SBPRO 0! 
+
+With "original" SB Pro cards, an initial setting of CD_volume through the
+sound cards MIXER register gets done.
+If you are using a "compatible" sound card of types "LaserMate" or "SPEA",
+you can set SOUND_BASE (in sbpcd.h) to get it done with your card, too...
+
+
+Using audio CDs:
+----------------
+
+Workman, WorkBone, xcdplayer, cdplayer and the nice little tool "cdplay" (see
+README.aztcd from the Aztech driver package) should work.
+
+The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer wants
+"/dev/rsr0", workman loves "/dev/sr0" or "/dev/cdrom" - so, do the appropriate
+links for using them without the need of supplying parameters.
+
+
+Copying audio tracks:
+---------------------
+
+The following program will copy track 1 (or a piece of it) from an audio CD
+into the file "track01":
+
+/*=================== begin program ========================================*/
+/*
+ * read an audio track from a CD
+ *
+ * (c) 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
+ *          may be used & enhanced freely
+ *
+ * Due to non-existent sync bytes at the beginning of each audio frame (or due
+ * to a firmware bug within all known drives?), it is currently a kind of
+ * fortune if two consecutive frames fit together.
+ * Usually, they overlap, or a little piece is missing. This happens in units
+ * of 24-byte chunks. It has to get fixed by higher-level software (reading
+ * until an overlap occurs, and then eliminate the overlapping chunks). 
+ * ftp.gwdg.de:/pub/linux/misc/cdda2wav-sbpcd.*.tar.gz holds an example of
+ * such an algorithm.
+ * This example program further is missing to obtain the SubChannel data
+ * which belong to each frame.
+ *
+ * This is only an example of the low-level access routine. The read data are
+ * pure 16-bit CDDA values; they have to get converted to make sound out of
+ * them.
+ * It is no fun to listen to it without prior overlap/underlap correction!
+ */
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+
+static struct cdrom_tochdr hdr;
+static struct cdrom_tocentry entry[101];
+static struct cdrom_read_audio arg;
+static u_char buffer[CD_FRAMESIZE_RAW];
+static int datafile, drive;
+static int i, j, limit, track, err;
+static char filename[32];
+
+main(int argc, char *argv[])
+{
+/*
+ * open /dev/cdrom
+ */
+  drive=open("/dev/cdrom", 0);
+  if (drive<0)
+    {
+      fprintf(stderr, "can't open drive.\n");
+      exit (-1);
+    }
+/*
+ * get TocHeader
+ */
+  fprintf(stdout, "getting TocHeader...\n");
+  err=ioctl(drive, CDROMREADTOCHDR, &hdr);
+  if (err!=0)
+    {
+      fprintf(stderr, "can't get TocHeader (error %d).\n", err);
+      exit (-1);
+    }
+  else
+    fprintf(stdout, "TocHeader: %d %d\n", hdr.cdth_trk0, hdr.cdth_trk1);
+/*
+ * get and display all TocEntries
+ */
+  fprintf(stdout, "getting TocEntries...\n");
+  for (i=1;i<=hdr.cdth_trk1+1;i++)
+    {
+      if (i!=hdr.cdth_trk1+1) entry[i].cdte_track = i;
+      else entry[i].cdte_track = CDROM_LEADOUT;
+      entry[i].cdte_format = CDROM_LBA;
+      err=ioctl(drive, CDROMREADTOCENTRY, &entry[i]);
+      if (err!=0)
+	{
+	  fprintf(stderr, "can't get TocEntry #%d (error %d).\n", i, err);
+	  exit (-1);
+	}
+      else
+	{
+	  fprintf(stdout, "TocEntry #%d: %1X %1X %06X %02X\n",
+		 entry[i].cdte_track,
+		 entry[i].cdte_adr,
+		 entry[i].cdte_ctrl,
+		 entry[i].cdte_addr.lba,
+		 entry[i].cdte_datamode);
+	}
+    }
+  fprintf(stdout, "got all TocEntries.\n");
+/*
+ * ask for track number (not implemented here)
+ */
+track=1;
+#if 0 /* just read a little piece (4 seconds) */
+entry[track+1].cdte_addr.lba=entry[track].cdte_addr.lba+300;
+#endif
+/*
+ * read track into file
+ */
+  sprintf(filename, "track%02d\0", track);
+  datafile=creat(filename, 0755);
+  if (datafile<0)
+    {
+      fprintf(stderr, "can't open datafile %s.\n", filename);
+      exit (-1);
+    }
+  arg.addr.lba=entry[track].cdte_addr.lba;
+  arg.addr_format=CDROM_LBA; /* CDROM_MSF would be possible here, too. */
+  arg.nframes=1;
+  arg.buf=&buffer[0];
+  limit=entry[track+1].cdte_addr.lba;
+  for (;arg.addr.lba<limit;arg.addr.lba++)
+    {
+      err=ioctl(drive, CDROMREADAUDIO, &arg);
+      if (err!=0)
+	{
+	  fprintf(stderr, "can't read abs. frame #%d (error %d).\n", 
+		 arg.addr.lba, err);
+	}
+      j=write(datafile, &buffer[0], CD_FRAMESIZE_RAW);
+      if (j!=CD_FRAMESIZE_RAW)
+	{
+	  fprintf(stderr,"I/O error (datafile) at rel. frame %d\n",
+			 arg.addr.lba-entry[track].cdte_addr.lba);
+	}
+      arg.addr.lba++;
+    }
+}
+/*===================== end program ========================================*/
+
+At ftp.gwdg.de:/pub/linux/misc/cdda2wav-sbpcd.*.tar.gz is an adapted version of
+Heiko Eissfeldt's digital-audio to .WAV converter (the original is there, too).
+This is preliminary, as Heiko himself will care about it.
+
+
+Known problems:
+---------------
+
+Currently, the detection of disk change or removal is actively disabled.
+
+Most attempts to read the UPC/EAN code result in a stream of zeroes. All my
+drives are mostly telling there is no UPC/EAN code on disk or there is, but it
+is an all-zero number. I guess now almost no CD holds such a number.
+
+Bug reports, comments, wishes, donations (technical information is a donation,
+too :-) etc. to
+                         emoenke@gwdg.de
+ or to my FIDO address:  Eberhard Moenkeberg, 2:2437/210.27
+
+SnailMail address, preferable for CD editors if they want to submit a free
+"cooperation" copy:
+                         Eberhard Moenkeberg
+                         Reinholdstr. 14
+                         D-37083 Goettingen
+                         Germany
+---
+
+
+Appendix -- the "cdtester" utility:
+
+/*
+ * cdtester.c -- test the audio functions of a CD driver
+ *
+ * (c) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>
+ *          published under the GPL
+ *
+ *          made under heavy use of the "Tiny Audio CD Player"
+ *          from Werner Zimmermann <zimmerma@rz.fht-esslingen.de>
+ *          (see linux/drivers/block/README.aztcd)
+ */
+#undef AZT_PRIVATE_IOCTLS /* not supported by every CDROM driver */
+#define SBP_PRIVATE_IOCTLS /* not supported by every CDROM driver */
+
+#include <stdio.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+
+#ifdef AZT_PRIVATE_IOCTLS
+#include <linux/aztcd.h>
+#endif AZT_PRIVATE_IOCTLS
+#ifdef SBP_PRIVATE_IOCTLS
+#include <linux/sbpcd.h>
+#include <linux/fs.h>
+#endif SBP_PRIVATE_IOCTLS
+
+struct cdrom_tochdr hdr;
+struct cdrom_tochdr tocHdr;
+struct cdrom_tocentry TocEntry[101];
+struct cdrom_tocentry entry;
+struct cdrom_multisession ms_info;
+struct cdrom_read_audio read_audio;
+struct cdrom_ti ti;
+struct cdrom_subchnl subchnl;
+struct cdrom_msf msf;
+struct cdrom_volctrl volctrl;
+#ifdef AZT_PRIVATE_IOCTLS
+union
+{
+	struct cdrom_msf msf;
+	unsigned char buf[CD_FRAMESIZE_RAW];
+} azt;
+#endif AZT_PRIVATE_IOCTLS
+int i, i1, i2, i3, j, k;
+unsigned char sequence=0;
+unsigned char command[80];
+unsigned char first=1, last=1;
+char *default_device="/dev/cdrom";
+char dev[20];
+char filename[20];
+int drive;
+int datafile;
+int rc;
+
+void help(void)
+{
+	printf("Available Commands:\n");
+	printf("STOP          s      EJECT        e       QUIT         q\n");
+	printf("PLAY TRACK    t      PAUSE        p       RESUME       r\n");
+	printf("NEXT TRACK    n      REPEAT LAST  l       HELP         h\n");
+	printf("SUBCHANNEL_Q  c      TRACK INFO   i       PLAY AT      a\n");
+	printf("READ          d      READ RAW     w       READ AUDIO   A\n");
+	printf("MS-INFO       M      TOC          T       START        S\n");
+	printf("SET EJECTSW   X      DEVICE       D       DEBUG        Y\n");
+	printf("AUDIO_BUFSIZ  Z      RESET        R       BLKRASET     B\n");
+	printf("SET VOLUME    v      GET VOLUME   V\n");
+}
+
+/*
+ *  convert MSF number (3 bytes only) to Logical_Block_Address 
+ */
+int msf2lba(u_char *msf)
+{
+	int i;
+	
+	i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
+	if (i<0) return (0);
+	return (i);
+}
+/*
+ *  convert logical_block_address to m-s-f_number (3 bytes only)
+ */
+void lba2msf(int lba, unsigned char *msf)
+{
+	lba += CD_BLOCK_OFFSET;
+	msf[0] = lba / (CD_SECS*CD_FRAMES);
+	lba %= CD_SECS*CD_FRAMES;
+	msf[1] = lba / CD_FRAMES;
+	msf[2] = lba % CD_FRAMES;
+}
+
+int init_drive(char *dev)
+{
+	unsigned char msf_ent[3];
+
+	/*
+	 * open the device
+	 */
+	drive=open(dev,0);
+	if (drive<0) return (-1);
+	/*
+	 * get TocHeader
+	 */
+	printf("getting TocHeader...\n");
+	rc=ioctl(drive,CDROMREADTOCHDR,&hdr);
+	if (rc!=0)
+	{
+		printf("can't get TocHeader (error %d).\n",rc);
+		return (-2);
+	}
+	else
+		first=hdr.cdth_trk0;
+		last=hdr.cdth_trk1;
+		printf("TocHeader: %d %d\n",hdr.cdth_trk0,hdr.cdth_trk1);
+	/*
+	 * get and display all TocEntries
+	 */
+	printf("getting TocEntries...\n");
+	for (i=1;i<=hdr.cdth_trk1+1;i++)
+	{
+		if (i!=hdr.cdth_trk1+1) TocEntry[i].cdte_track = i;
+		else TocEntry[i].cdte_track = CDROM_LEADOUT;
+		TocEntry[i].cdte_format = CDROM_LBA;
+		rc=ioctl(drive,CDROMREADTOCENTRY,&TocEntry[i]);
+		if (rc!=0)
+		{
+			printf("can't get TocEntry #%d (error %d).\n",i,rc);
+		}
+		else
+		{
+			lba2msf(TocEntry[i].cdte_addr.lba,&msf_ent[0]);
+			if (TocEntry[i].cdte_track==CDROM_LEADOUT)
+			{
+				printf("TocEntry #%02X: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
+				       TocEntry[i].cdte_track,
+				       TocEntry[i].cdte_adr,
+				       TocEntry[i].cdte_ctrl,
+				       msf_ent[0],
+				       msf_ent[1],
+				       msf_ent[2],
+				       TocEntry[i].cdte_addr.lba,
+				       TocEntry[i].cdte_datamode);
+			}
+			else
+			{
+				printf("TocEntry #%02d: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
+				       TocEntry[i].cdte_track,
+				       TocEntry[i].cdte_adr,
+				       TocEntry[i].cdte_ctrl,
+				       msf_ent[0],
+				       msf_ent[1],
+				       msf_ent[2],
+				       TocEntry[i].cdte_addr.lba,
+				       TocEntry[i].cdte_datamode);
+			}
+		}
+	}
+	return (hdr.cdth_trk1); /* number of tracks */
+}
+
+void display(int size,unsigned char *buffer)
+{
+	k=0;
+	getchar();
+	for (i=0;i<(size+1)/16;i++)
+	{
+		printf("%4d:",i*16);
+		for (j=0;j<16;j++)
+		{
+			printf(" %02X",buffer[i*16+j]);
+		}
+		printf("  ");
+		for (j=0;j<16;j++)
+		{
+			if (isalnum(buffer[i*16+j])) 
+				printf("%c",buffer[i*16+j]);
+			else
+				printf(".");
+		}
+		printf("\n"); 
+		k++;
+		if (k>=20)
+		{
+			printf("press ENTER to continue\n");
+			getchar();
+			k=0;
+		}
+	} 
+} 
+
+main(int argc, char *argv[])
+{
+	printf("\nTesting tool for a CDROM driver's audio functions V0.1\n");
+	printf("(C) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>\n");
+	printf("initializing...\n");
+	
+	rc=init_drive(default_device);
+	if (rc<0) printf("could not open %s (rc=%d).\n",default_device,rc);
+	help();
+	while (1)
+	{
+		printf("Give a one-letter command (h = help): ");
+		scanf("%s",command);
+		command[1]=0;
+		switch (command[0])
+		{
+		case 'D':
+			printf("device name (f.e. /dev/sbpcd3): ? ");
+			scanf("%s",&dev);
+			close(drive);
+			rc=init_drive(dev);
+			if (rc<0) printf("could not open %s (rc %d).\n",dev,rc);
+			break;
+		case 'e':
+			rc=ioctl(drive,CDROMEJECT);
+			if (rc<0) printf("CDROMEJECT: rc=%d.\n",rc);
+			break;
+		case 'p':
+			rc=ioctl(drive,CDROMPAUSE);
+			if (rc<0) printf("CDROMPAUSE: rc=%d.\n",rc);
+			break;
+		case 'r':
+			rc=ioctl(drive,CDROMRESUME);
+			if (rc<0) printf("CDROMRESUME: rc=%d.\n",rc);
+			break;
+		case 's':
+			rc=ioctl(drive,CDROMSTOP);
+			if (rc<0) printf("CDROMSTOP: rc=%d.\n",rc);
+			break;
+		case 'S':
+			rc=ioctl(drive,CDROMSTART);
+			if (rc<0) printf("CDROMSTART: rc=%d.\n",rc);
+			break;
+		case 't':
+			rc=ioctl(drive,CDROMREADTOCHDR,&tocHdr);
+			if (rc<0)
+			{
+				printf("CDROMREADTOCHDR: rc=%d.\n",rc);
+				break;
+			}
+			first=tocHdr.cdth_trk0;
+			last= tocHdr.cdth_trk1;
+			if ((first==0)||(first>last))
+			{
+				printf ("--got invalid TOC data.\n");
+			}
+			else
+			{
+				printf("--enter track number(first=%d, last=%d): ",first,last);
+				scanf("%d",&i1);
+				ti.cdti_trk0=i1;
+				if (ti.cdti_trk0<first) ti.cdti_trk0=first;
+				if (ti.cdti_trk0>last) ti.cdti_trk0=last;
+				ti.cdti_ind0=0;
+				ti.cdti_trk1=last;
+				ti.cdti_ind1=0;
+				rc=ioctl(drive,CDROMSTOP);
+				rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
+				if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
+			}
+			break;
+		case 'n':
+			rc=ioctl(drive,CDROMSTOP);
+			if (++ti.cdti_trk0>last) ti.cdti_trk0=last;
+			ti.cdti_ind0=0;
+			ti.cdti_trk1=last;
+			ti.cdti_ind1=0;
+			rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
+			if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
+			break;
+		case 'l':
+			rc=ioctl(drive,CDROMSTOP);
+			if (--ti.cdti_trk0<first) ti.cdti_trk0=first;
+			ti.cdti_ind0=0;
+			ti.cdti_trk1=last;
+			ti.cdti_ind1=0;
+			rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
+			if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
+			break;
+		case 'c':
+			subchnl.cdsc_format=CDROM_MSF;
+			rc=ioctl(drive,CDROMSUBCHNL,&subchnl);
+			if (rc<0) printf("CDROMSUBCHNL: rc=%d.\n",rc);
+			else
+			{
+				printf("AudioStatus:%s  Track:%d  Mode:%d  MSF=%02d:%02d:%02d\n",
+				       subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",
+				       subchnl.cdsc_trk,subchnl.cdsc_adr, 
+				       subchnl.cdsc_absaddr.msf.minute,
+				       subchnl.cdsc_absaddr.msf.second,
+				       subchnl.cdsc_absaddr.msf.frame);
+			}
+			break;              
+		case 'i':
+			printf("Track No.: ");
+			scanf("%d",&i1);
+			entry.cdte_track=i1;
+			if (entry.cdte_track<first) entry.cdte_track=first;
+			if (entry.cdte_track>last)  entry.cdte_track=last;
+			entry.cdte_format=CDROM_MSF;
+			rc=ioctl(drive,CDROMREADTOCENTRY,&entry);
+			if (rc<0) printf("CDROMREADTOCENTRY: rc=%d.\n",rc);
+			else
+			{
+				printf("Mode %d Track, starts at %02d:%02d:%02d\n",
+				       entry.cdte_adr,
+				       entry.cdte_addr.msf.minute,
+				       entry.cdte_addr.msf.second,
+				       entry.cdte_addr.msf.frame);
+			}
+			break;
+		case 'a':
+			printf("Address (min:sec:frm)  ");
+			scanf("%d:%d:%d",&i1,&i2,&i3);
+			msf.cdmsf_min0=i1;
+			msf.cdmsf_sec0=i2;
+			msf.cdmsf_frame0=i3;
+			if (msf.cdmsf_sec0>59) msf.cdmsf_sec0=59;
+			if (msf.cdmsf_frame0>74) msf.cdmsf_frame0=74;
+			lba2msf(TocEntry[last+1].cdte_addr.lba-1,&msf.cdmsf_min1);
+			rc=ioctl(drive,CDROMSTOP);
+			rc=ioctl(drive,CDROMPLAYMSF,&msf);
+			if (rc<0) printf("CDROMPLAYMSF: rc=%d.\n",rc);
+			break;
+		case 'V':
+			rc=ioctl(drive,CDROMVOLREAD,&volctrl);
+			if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
+			printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
+			break;  
+		case 'R':
+			rc=ioctl(drive,CDROMRESET);
+			if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
+			break;
+		case 'B': /* set the driver's (?) read ahead value */
+			printf("enter read-ahead size: ? ");
+			scanf("%d",&i);
+			rc=ioctl(drive,BLKRASET,i);
+			if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
+			break;
+#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
+		case 'd':
+			printf("Address (min:sec:frm)  ");
+			scanf("%d:%d:%d",&i1,&i2,&i3);
+			azt.msf.cdmsf_min0=i1;
+			azt.msf.cdmsf_sec0=i2;
+			azt.msf.cdmsf_frame0=i3;
+			if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
+			if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
+			rc=ioctl(drive,CDROMREADMODE1,&azt.msf);
+			if (rc<0) printf("CDROMREADMODE1: rc=%d.\n",rc);
+			else display(CD_FRAMESIZE,azt.buf);
+			break;
+		case 'w':
+			printf("Address (min:sec:frame)  ");
+			scanf("%d:%d:%d",&i1,&i2,&i3);
+			azt.msf.cdmsf_min0=i1;
+			azt.msf.cdmsf_sec0=i2;
+			azt.msf.cdmsf_frame0=i3;
+			if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
+			if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
+			rc=ioctl(drive,CDROMREADMODE2,&azt.msf);
+			if (rc<0) printf("CDROMREADMODE2: rc=%d.\n",rc);
+			else display(CD_FRAMESIZE_RAW,azt.buf); /* currently only 2336 */
+			break;  
+#endif
+		case 'v':
+			printf("--Channel 0 (Left)  (0-255): ");
+			scanf("%d",&i1);
+			volctrl.channel0=i1;
+			printf("--Channel 1 (Right) (0-255): ");
+			scanf("%d",&i1);
+			volctrl.channel1=i1;
+			volctrl.channel2=0;
+			volctrl.channel3=0;
+			rc=ioctl(drive,CDROMVOLCTRL,&volctrl);
+			if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
+			break;  
+		case 'q':
+			close(drive);
+			exit(0);
+		case 'h':
+			help();
+			break;
+		case 'T': /* display TOC entry - without involving the driver */
+			scanf("%d",&i);
+			if ((i<hdr.cdth_trk0)||(i>hdr.cdth_trk1))
+				printf("invalid track number.\n");
+			else
+				printf("TocEntry %02d: adr=%01X ctrl=%01X msf=%02d:%02d:%02d mode=%02X\n",
+				       TocEntry[i].cdte_track,
+				       TocEntry[i].cdte_adr,
+				       TocEntry[i].cdte_ctrl,
+				       TocEntry[i].cdte_addr.msf.minute,
+				       TocEntry[i].cdte_addr.msf.second,
+				       TocEntry[i].cdte_addr.msf.frame,
+				       TocEntry[i].cdte_datamode);
+			break;
+		case 'A': /* read audio data into file */
+			printf("Address (min:sec:frm) ? ");
+			scanf("%d:%d:%d",&i1,&i2,&i3);
+			read_audio.addr.msf.minute=i1;
+			read_audio.addr.msf.second=i2;
+			read_audio.addr.msf.frame=i3;
+			read_audio.addr_format=CDROM_MSF;
+			printf("# of frames ? ");
+			scanf("%d",&i1);
+			read_audio.nframes=i1;
+			k=read_audio.nframes*CD_FRAMESIZE_RAW;
+			read_audio.buf=malloc(k);
+			if (read_audio.buf==NULL)
+			{
+				printf("can't malloc %d bytes.\n",k);
+				break;
+			}
+			sprintf(filename,"audio_%02d%02d%02d_%02d.%02d\0",
+				read_audio.addr.msf.minute,
+				read_audio.addr.msf.second,
+				read_audio.addr.msf.frame,
+				read_audio.nframes,
+				++sequence);
+			datafile=creat(filename, 0755);
+			if (datafile<0)
+			{
+				printf("can't open datafile %s.\n",filename);
+				break;
+			}
+			rc=ioctl(drive,CDROMREADAUDIO,&read_audio);
+			if (rc!=0)
+			{
+				printf("CDROMREADAUDIO: rc=%d.\n",rc);
+			}
+			else
+			{
+				rc=write(datafile,&read_audio.buf,k);
+				if (rc!=k) printf("datafile I/O error (%d).\n",rc);
+			}
+			close(datafile);
+			break;
+		case 'X': /* set EJECT_SW (0: disable, 1: enable auto-ejecting) */
+			scanf("%d",&i);
+			rc=ioctl(drive,CDROMEJECT_SW,i);
+			if (rc!=0)
+				printf("CDROMEJECT_SW: rc=%d.\n",rc);
+			else
+				printf("EJECT_SW set to %d\n",i);
+			break;
+		case 'M': /* get the multisession redirection info */
+			ms_info.addr_format=CDROM_LBA;
+			rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
+			if (rc!=0)
+			{
+				printf("CDROMMULTISESSION(lba): rc=%d.\n",rc);
+			}
+			else
+			{
+				if (ms_info.xa_flag) printf("MultiSession offset (lba): %d (0x%06X)\n",ms_info.addr.lba,ms_info.addr.lba);
+				else
+				{
+					printf("this CD is not an XA disk.\n");
+					break;
+				}
+			}
+			ms_info.addr_format=CDROM_MSF;
+			rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
+			if (rc!=0)
+			{
+				printf("CDROMMULTISESSION(msf): rc=%d.\n",rc);
+			}
+			else
+			{
+				if (ms_info.xa_flag)
+					printf("MultiSession offset (msf): %02d:%02d:%02d (0x%02X%02X%02X)\n",
+					       ms_info.addr.msf.minute,
+					       ms_info.addr.msf.second,
+					       ms_info.addr.msf.frame,
+					       ms_info.addr.msf.minute,
+					       ms_info.addr.msf.second,
+					       ms_info.addr.msf.frame);
+				else printf("this CD is not an XA disk.\n");
+			}
+			break;
+#ifdef SBP_PRIVATE_IOCTLS
+		case 'Y': /* set the driver's message level */
+#if 0 /* not implemented yet */
+			printf("enter switch name (f.e. DBG_CMD): ");
+			scanf("%s",&dbg_switch);
+			j=get_dbg_num(dbg_switch);
+#else
+			printf("enter DDIOCSDBG switch number: ");
+			scanf("%d",&j);
+#endif
+			printf("enter 0 for \"off\", 1 for \"on\": ");
+			scanf("%d",&i);
+			if (i==0) j|=0x80;
+			printf("calling \"ioctl(drive,DDIOCSDBG,%d)\"\n",j);
+			rc=ioctl(drive,DDIOCSDBG,j);
+			printf("DDIOCSDBG: rc=%d.\n",rc);
+			break;
+		case 'Z': /* set the audio buffer size */
+			printf("# frames wanted: ? ");
+			scanf("%d",&j);
+			rc=ioctl(drive,CDROMAUDIOBUFSIZ,j);
+			printf("%d frames granted.\n",rc);
+			break;
+#endif SBP_PRIVATE_IOCTLS
+		default:
+			printf("unknown command: \"%s\".\n",command);
+			break;
+		}
+	}
+}
+/*==========================================================================*/
+
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/sjcd linux/Documentation/cdrom/sjcd
--- v1.3.35/linux/Documentation/cdrom/sjcd	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/sjcd	Mon Oct 16 15:23:25 1995
@@ -0,0 +1,80 @@
+ -- README.sjcd
+				80% of the work takes 20% of the time,
+				20% of the work takes 80% of the time...
+						(Murphy law)
+
+				Once started, training can not be stopped...
+						(StarWars)
+
+This is the README for the sjcd cdrom driver, version 1.5.
+
+This file is meant as a tips & tricks edge for the usage of the SANYO CDR-H94A
+cdrom drive. It will grow as the questions arise. ;-)
+Since the drive often comes with an ISP16 soundcard, which can be used
+as cdrom interface, this is also the place for ISP16 related issues.
+
+The driver should work with any SoundBlaster/Panasonic style CDROM interface,
+including the "soft configurable" MediaMagic sound card.
+To make this sound card (and others like "Mozart") working, it has to get 
+"configured" by software.
+The suggestion to configure the ISP16 soundcard by booting DOS and 
+a warm reboot to boot Linux somehow doesn't work, at least not
+on Eric's machine (IPC P90), with the version of the ISP16
+card he has (there appear to be at least two versions: Eric's card with
+no jumpered IDE support and OPTi 82C928 chip, and Vadim's version
+with a jumper to enable IDE support, probably with a OPTi 82C929 chip).
+Therefore detection and configuration of the ISP16 interfaces is included
+in the driver.
+If we should support any other interfaces (which cannot be configured
+through DOS) or if there are any more ISP16 types, please let us
+know (maarel@marin.nl) and we'll see.
+
+Otherwise, you should boot DOS once (and by this, run the "configuration driver")
+and then switch to Linux by use of CTRL-ALT-DEL. Each use of the RESET
+button or the power switch makes this procedure necessary again.
+If no ISP16 is detected, there's no harm done; a card configured trough DOS
+may still work as expected.
+
+As of version 1.4 sound through the speakers is supported; only for MSS-mode 
+and no volume controle yet.
+
+PAUSE and STOP ioctl commands don't seem to work yet.
+
+ISP16 configuration routines reside at Vadim's server
+      ftp.rbrf.ru:/linux/mediamagic/
+and at Eberhard's mirror
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
+
+Leo Spiekman's configuration routines for the ISP-16 card can get found at
+      dutette.et.tudelft.nl:/pub/linux/
+and at Eberhard's mirror
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
+
+Eric van der Maarel's routines are included in sjcd.c.
+This, and any related stuff may be found by anonymous ftp at
+      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
+
+The device major for sjcd is 18, and minor is 0. Create a block special
+file in your /dev directory (e.g., /dev/sjcd) with these numbers.
+(For those who don't know, being root and doing the following should do the trick:
+  mknod -m 644 /dev/sjcd b 18 0
+and mount the cdrom by /dev/sjcd).
+
+The default configuration parameters are:
+  base address 0x340
+  no irq
+  no dma
+As of version 1.2, setting base address, irq and dma at boot time is supported
+through the use of command line options: type at the "boot:" prompt:
+  linux sjcd=<base address>,<irq>,<dma>
+(where your kernel is assumed to be called by saying "linux" to
+the boot manager).
+
+If something is wrong, e-mail to		vadim@rbrf.ru
+					or 	vadim@ipsun.ras.ru
+					or	model@cecmow.enet.dec.com
+
+It happens sometimes that Vadim is not reachable by mail. For these
+instances, Eric van der Maarel (maarel@marin.nl) will help, too.
+
+		Vadim V. Model, Eric van der Maarel, Eberhard Moenkeberg
diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/sonycd535 linux/Documentation/cdrom/sonycd535
--- v1.3.35/linux/Documentation/cdrom/sonycd535	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/sonycd535	Sat Jul  1 19:05:58 1995
@@ -0,0 +1,121 @@
+              README FOR LINUX SONY CDU-535/531 DRIVER
+              ========================================
+
+This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux.
+I do not think I have the documentation to add features like DMA support
+so if anyone else wants to pursue it or help me with it, please do.
+(I need to see what was done for the CDU-31A driver -- perhaps I can
+steal some of that code.)
+
+This is a Linux device driver for the Sony CDU-535 CDROM drive.  This is
+one of the older Sony drives with its own interface card (Sony bus).
+The DOS driver for this drive is named SONY_CDU.SYS - when you boot DOS
+your drive should be identified as a SONY CDU-535.  The driver works
+with a CDU-531 also.  One user reported that the driver worked on drives
+OEM'ed by Procomm, drive and interface board were labelled Procomm.
+
+The Linux driver is based on Corey Minyard's sonycd 0.3 driver for
+the CDU-31A.  Ron Jeppesen just changed the commands that were sent
+to the drive to correspond to the CDU-535 commands and registers.
+There were enough changes to let bugs creep in but it seems to be stable.
+Ron was able to tar an entire CDROM (should read all blocks) and built
+ghostview and xfig off Walnut Creek's X11R5/GNU CDROM.  xcdplayer and
+workman work with the driver.  Others have used the driver without
+problems except those dealing with wait loops (fixed in third release).
+Like Minyard's original driver this one uses a polled interface (this
+is also the default setup for the DOS driver).  It has not been tried
+with interrupts or DMA enabled on the board.
+
+REQUIREMENTS
+============
+
+	- Sony CDU-535 drive, preferably without interrupts and DMA 
+	  enabled on the card.
+
+	- Drive must be set up as unit 1.  Only the first unit will be 
+	  recognized
+
+	- you must enter your interface address into 
+          /usr/src/linux/include/linux/sonycd535.h and build the
+          appropriate kernel or use the "kernel command line" parameter
+                sonycd535=0x320
+          with the correct interface address.
+
+NOTES:
+======
+
+1) The drive MUST be turned on when booting or it will not be recognized!
+   (but see comments on modularized version below)
+
+2) when the cdrom device is opened the eject button is disabled to keep the
+   user from ejecting a mounted disk and replacing it with another.
+   Unfortunately xcdplayer and workman also open the cdrom device so you
+   have to use the eject button in the software.  Keep this in mind if your
+   cdrom player refuses to give up its disk -- exit workman or xcdplayer, or
+   umount the drive if it has been mounted.
+
+THANKS
+======
+
+Many thanks to Ron Jeppesen (ronj.an@site007.saic.com) for getting
+this project off the ground.  He wrote the initial release
+and the first two patches to this driver (0.1, 0.2, and 0.3).
+Thanks also to Eberhard Moenkeberg (emoenke@gwdg.de) for prodding
+me to place this code into the mainstream Linux source tree
+(as of Linux version 1.1.91), as well as some patches to make
+it a better device citizen.  Further thanks to "S. Joel Katz"
+<stimpson@panix.com> for his MODULE patches (see details below),
+Porfiri Claudio <C.Porfiri@nisms.tei.ericsson.se> for patches
+to make the driver work with the older CDU-510/515 series, and
+Heiko Eissfeldt <heiko@colossus.escape.de> for pointing out that
+the verify_area() checks were ignoring the results of said checks.
+
+(Acknowledgments from Ron Jeppesen in the 0.3 release:)
+Thanks to Corey Minyard who wrote the original CDU-31A driver on which
+this driver is based.  Thanks to Ken Pizzini and Bob Blair who provided 
+patches and feedback on the first release of this driver.
+
+Ken Pizzini
+ken@halcyon.com
+
+------------------------------------------------------------------------------
+(The following is from Joel Katz <Stimpson@Panix.COM>.)
+
+	To build a version of sony535.o that can be installed as a module,
+use the following command:
+
+gcc -c -D__KERNEL__ -DMODULE -O2 sonycd535.c -o sonycd535.o
+
+	To install the module, simply type:
+
+insmod sony535.o
+	or
+insmod sony535.o sonycd535=<address>
+
+	And to remove it:
+
+rmmod sony535
+
+	The code checks to see if MODULE is defined and behaves as it used
+to if MODULE is not defined. That means your patched file should behave
+exactly as it used to if compiled into the kernel.
+
+	I have an external drive, and I usually leave it powered off. I used
+to have to reboot if I needed to use the CDROM drive. Now I don't.
+
+	Even if you have an internal drive, why waste the 268K of memory
+(unswappable) that the driver uses if you use your CD-ROM drive infrequently?
+
+	This driver will not install (whether compiled in or loaded as a
+module) if the CDROM drive is not available during its initialization. This
+means that you can have the driver compiled into the kernel and still load
+the module later (assuming the driver doesn't install itself during
+power-on). This only wastes 12K when you boot with the CDROM drive off.
+
+	This is what I usually do; I leave the driver compiled into the
+kernel, but load it as a module if I powered the system up with the drive
+off and then later decided to use the CDROM drive.
+
+	Since the driver only uses a single page to point to the chunks,
+attempting to set the buffer cache to more than 2 Megabytes would be very
+bad; don't do that.
diff -u --recursive --new-file v1.3.35/linux/MAGIC linux/MAGIC
--- v1.3.35/linux/MAGIC	Mon Oct 16 18:38:21 1995
+++ linux/MAGIC	Fri Oct 20 10:10:39 1995
@@ -36,39 +36,62 @@
 					Theodore Ts'o
 					31-Mar-94
 
+The magic table is current to Linux 1.3.35.
+The ioctl table is current to Linux 1.3.35.
+For a complete list of kernel ioctl's, look for 'ioctl-list.X.Y.ZZ' on
+an ftp site, where 'X.Y.ZZ' is the kernel version for the ioctl list.
+
+					Michael Chastain
+					<mec@duracef.shout.net>
+					17-Oct-95
+
+
+
+
 Magic Name          Number  Structure            File
 ===========================================================================
+CYCLADES_MAGIC      0x4359  struct cyclades_port include/linux/cyclades.h
 FASYNC_MAGIC        0x4601  struct fasync_struct include/linux/fs.h
 PTY_MAGIC           0x5001  struct pty_struct    drivers/char/pty.c
-PPP_MAGIC           0x5002  struct ppp_struct    include/linux/ppp.h
+PPP_MAGIC           0x5002  struct ppp_struct    include/linux/if_ppp.h
 TTY_MAGIC           0x5401  struct tty_struct    include/linux/tty.h
 TTY_DRIVER_MAGIC    0x5402  struct tty_driver    include/linux/tty_driver.h
 TTY_LDISC_MAGIC     0x5403  struct tty_ldisc     include/linux/tty_ldisc.h
 SERIAL_MAGIC        0x5301  struct async_struct  include/linux/serial.h
 SLIP_MAGIC          0x5302  struct slip          drivers/net/slip.h
+SCC_MAGIC           0x8530  struct scc_channel   include/linux/scc.h
 
-Ioctl	Include File	Comments
-========================================================
-0x00	fd.h
-0x01	random.h	subcodes starting at 0x080000
-0x03	hdreg.h
-0x06	lp.h
-0x12	fs.h
-'C'	soundcard.h
-'K'	kd.h
-'M'	soundcard.h
-'P'	soundcard.h
-'Q'	soundcard.h
-'S'	cdrom.h
-'S'	fs.h		subcodes from 0x80, no conflict
-'T'	soundcard.h	conflicts with termios.h
-'T'	termios.h	conflicts with soundcard.h
-'T'	ppp.h		subcodes from 0x90, no conflict
-'V'	vt.h
-'f'	ext2_fs.h
-'m'	mtio.h		conflicts with soundcard.h
-'m'	soundcard.h	conflicts with mtio.h
-'s'	pcsp.h
-'v'	ext2_fs.h
-0x89	sockios.h
 
+
+Ioctl	Include File		Comments
+========================================================
+0x00	linux/fs.h		only FIBMAP, FIGETBSZ
+0x00	linux/random.h		codes in 0x010800NN
+0x02	linux/fd.h
+0x03	linux/hdreg.h
+0x04	linux/umsdos_fs.h
+0x06	linux/lp.h
+0x12	linux/fs.h
+0x20	linux/cm206.h
+'C'	linux/soundcard.h
+'K'	linux/kd.h
+'M'	linux/soundcard.h
+'P'	linux/soundcard.h
+'Q'	linux/soundcard.h
+'S'	linux/cdrom.h		conflict!
+'S'	linux/scsi.h		conflict!
+'T'	linux/soundcard.h	conflict!
+'T'	linux/scc.h		conflict!
+'T'	asm/termios.h		conflict!
+'V'	linux/vt.h
+'Y'	linux/cyclades.h	codes in 0x004359NN
+'f'	linux/ext2_fs.h
+'m'	linux/mtio.h		conflict!
+'m'	linux/soundcard.h	conflict!
+'s'	linux/cdk.h
+'t'	linux/if_ppp.h
+'u'	linux/smb_fs.h
+'u'	linux/smb_fs.h
+'v'	linux/ext2_fs.h
+0x89	asm/socket.h		no conflict
+0x89	linux/sockios.h		no conflict
diff -u --recursive --new-file v1.3.35/linux/Makefile linux/Makefile
--- v1.3.35/linux/Makefile	Mon Oct 16 18:38:21 1995
+++ linux/Makefile	Sat Oct 21 17:49:54 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 35
+SUBLEVEL = 36
 
 ARCH = i386
 
@@ -94,7 +94,11 @@
 LIBS		=$(TOPDIR)/lib/lib.a
 SUBDIRS		=kernel drivers mm fs net ipc lib
 
-ifdef CONFIG_SCSI
+ifdef CONFIG_CD_NO_IDESCSI
+DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
+endif
+
+ifeq ($(CONFIG_SCSI),y)
 DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
 endif
 
diff -u --recursive --new-file v1.3.35/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.35/linux/arch/alpha/config.in	Mon Oct 16 18:38:21 1995
+++ linux/arch/alpha/config.in	Sun Oct 22 22:17:41 1995
@@ -13,23 +13,6 @@
   define_bool CONFIG_NATIVE y
 fi
 
-tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
-bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y
-if [ "$CONFIG_ST506" = "y" ]; then
-  comment 'Please see drivers/block/README.ide for help/info on IDE drives'
-  bool '   Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD n
-  if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
-    bool '   Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n
-  else
-    bool '   Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y
-  fi
-  if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
-    bool '   Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n
-  fi
-fi
-
-bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
-bool 'Networking support' CONFIG_NET y
 choice 'Alpha system type' \
 	"Jensen		CONFIG_ALPHA_JENSEN		\
 	 Noname		CONFIG_ALPHA_NONAME		\
@@ -47,295 +30,72 @@
 if [ "$CONFIG_ALPHA_CABRIOLET" = "y" \
 	-o "$CONFIG_ALPHA_EB64" = "y" -o "$CONFIG_ALPHA_EB64P" = "y" ]
 then
-	bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM n
+	bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM
 	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_APECS y
 fi
 
 if [ "$CONFIG_PCI" = "y" ]; then
-  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE n
-  if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
-    bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON y
-  fi
+  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE
 fi
-bool 'System V IPC' CONFIG_SYSVIPC y
-tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n
+bool 'Networking support' CONFIG_NET
+bool 'System V IPC' CONFIG_SYSVIPC
+tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 
+mainmenu_option next_comment
 comment 'Loadable module support'
-bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
+bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
+
+source drivers/block/Config.in
 
 if [ "$CONFIG_NET" = "y" ]; then
-mainmenu_option next_comment
-comment 'Networking options'
-bool 'TCP/IP networking' CONFIG_INET y
-if [ "$CONFIG_INET" = "y" ]; then
-bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n
-bool 'IP: multicasting' CONFIG_IP_MULTICAST n
-bool 'IP: firewalling' CONFIG_IP_FIREWALL n
-bool 'IP: accounting' CONFIG_IP_ACCT n
-tristate 'IP: tunneling' CONFIG_NET_IPIP n
-if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then
-  bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y
-  bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n
-fi
-if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_MULTICAST" = "y" -a "$CONFIG_NET_IPIP" = "y" ]; then
-  bool 'IP: multicast routing(in progress)' CONFIG_IP_MROUTE n
-fi
-comment '(it is safe to leave these untouched)'
-bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n
-tristate 'IP: Reverse ARP' CONFIG_INET_RARP n
-bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y
-bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
-bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y
-bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE y
-fi
-bool 'The IPX protocol' CONFIG_IPX n
-bool 'Appletalk DDP' CONFIG_ATALK n
-bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
-if [ "$CONFIG_AX25" = "y" ]; then
-  bool 'Amateur Radio NET/ROM' CONFIG_NETROM n
-fi
+  source net/Config.in
 fi
 
 mainmenu_option next_comment
 comment 'SCSI support'
 
-tristate 'SCSI support' CONFIG_SCSI y
-
-if [ "$CONFIG_SCSI" = "n" ]; then
-
-comment 'Skipping SCSI configuration options...'
-
-else
+tristate 'SCSI support' CONFIG_SCSI
 
-comment 'SCSI support type (disk, tape, CDrom)'
-
-dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD y $CONFIG_SCSI
-dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST n $CONFIG_SCSI
-dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR y $CONFIG_SCSI
-dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG n $CONFIG_SCSI
-
-comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
-
-bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n
-
-mainmenu_option next_comment
-comment 'SCSI low-level drivers'
-
-dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n $CONFIG_SCSI
-dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n $CONFIG_SCSI
-dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n $CONFIG_SCSI
-dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX n $CONFIG_SCSI
-dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n $CONFIG_SCSI
-dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA n $CONFIG_SCSI
-dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO n $CONFIG_SCSI
-dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n $CONFIG_SCSI
-dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n $CONFIG_SCSI
-bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n
-if [ "$CONFIG_PCI" = "y" ]; then
-  dep_tristate 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx y $CONFIG_SCSI
-fi
-dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n $CONFIG_SCSI
-bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n
-dep_tristate 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n $CONFIG_SCSI
-dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n $CONFIG_SCSI
-bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n
-dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n $CONFIG_SCSI
-dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST n $CONFIG_SCSI
-dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n $CONFIG_SCSI
-#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n $CONFIG_SCSI
+if [ "$CONFIG_SCSI" != "n" ]; then
+  source drivers/scsi/Config.in
 fi
 
-
 if [ "$CONFIG_NET" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'Network device support'
 
-mainmenu_option next_comment
-comment 'Network device support'
-
-bool 'Network device support' CONFIG_NETDEVICES y
-if [ "$CONFIG_NETDEVICES" = "n" ]; then
-
-comment 'Skipping network driver configuration options...'
-
-else
-tristate 'Dummy net driver support' CONFIG_DUMMY n
-tristate 'SLIP (serial line) support' CONFIG_SLIP n
-if [ "$CONFIG_SLIP" != "n" ]; then
-  bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
-fi
-tristate 'PPP (point-to-point) support' CONFIG_PPP n
-if [ ! "$CONFIG_PPP" = "n" ]; then
-comment 'CCP compressors for PPP are only built as modules.'
-fi
-if [ "$CONFIG_AX25" = "y" ]; then
-	bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC y
-else
-	bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC n
-fi
-tristate 'PLIP (parallel port) support' CONFIG_PLIP n
-tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n
-bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
-bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
-if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
-	tristate 'WD80*3 support' CONFIG_WD80x3 n
-	tristate 'SMC Ultra support' CONFIG_ULTRA n
-fi
-bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n
-bool '3COM cards' CONFIG_NET_VENDOR_3COM n
-if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
-	tristate '3c501 support' CONFIG_EL1 n
-	tristate '3c503 support' CONFIG_EL2 n
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		tristate '3c505 support' CONFIG_ELPLUS n
-		tristate '3c507 support' CONFIG_EL16 n
-	fi
-	tristate '3c509/3c579 support' CONFIG_EL3 y
-fi
-bool 'Other ISA cards' CONFIG_NET_ISA n
-if [ "$CONFIG_NET_ISA" = "y" ]; then
-	tristate 'Cabletron E21xx support' CONFIG_E2100 n
-	tristate 'DEPCA support' CONFIG_DEPCA y
-	tristate 'EtherWorks 3 support' CONFIG_EWRK3 n
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		bool 'SEEQ8005 support' CONFIG_SEEQ8005 n
-		tristate 'AT1700 support' CONFIG_AT1700 n
-		tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n
-		tristate 'EtherExpress support' CONFIG_EEXPRESS n
-		bool 'NI5210 support' CONFIG_NI52 n
-		bool 'NI6510 support' CONFIG_NI65 n
-		if [ "$CONFIG_AX25" = "y" ]; then
-			bool 'Ottawa PI and PI/2 support' CONFIG_PI y
-		fi
-		tristate 'WaveLAN support' CONFIG_WAVELAN n
-	fi
-	tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n
-	tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n
-	tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 n
-	tristate 'NE2000/NE1000 support' CONFIG_NE2000 n
-	bool 'SK_G16 support' CONFIG_SK_G16 n
-fi
-bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA y
-if [ "$CONFIG_NET_EISA" = "y" ]; then
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		tristate 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
-	fi
-	tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
-	tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5 y
-#	tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP n
-#	bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n
-#	bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n
-	bool 'Zenith Z-Note support' CONFIG_ZNET n
-fi
-bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n
-if [ "$CONFIG_NET_POCKET" = "y" ]; then
-	bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
-	tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
-	tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n
-#	bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n
-#	bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n
-#	bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n
-fi
-bool 'Token Ring driver support' CONFIG_TR n
-if [ "$CONFIG_TR" = "y" ]; then
-	tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR y
-fi
-tristate 'Arcnet support' CONFIG_ARCNET n
-fi
-fi
-
-mainmenu_option next_comment
-comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
-
-bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI n
-if [ "$CONFIG_CD_NO_IDESCSI" = "y" ]; then
-  tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A n
-  tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD n
-  tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX n
-  tristate 'Matsushita/Panasonic CDROM support' CONFIG_SBPCD n
-  if [ "$CONFIG_SBPCD" = "y" ]; then
-    bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
-    if [ "$CONFIG_SBPCD2" = "y" ]; then
-      bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
-      if [ "$CONFIG_SBPCD3" = "y" ]; then
-        bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
-      fi
-    fi
+  bool 'Network device support' CONFIG_NETDEVICES
+  if [ "$CONFIG_NETDEVICES" = "y" ]; then
+    source drivers/net/Config.in
   fi
-  tristate 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
-  tristate 'Sony CDU535 CDROM support' CONFIG_CDU535 n
-  tristate 'Goldstar R420 CDROM support' CONFIG_GSCD n
-  tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206 n
-  tristate 'Experimental Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD n
-  bool 'Experimental Sanyo H94A CDROM support' CONFIG_SJCD n
 fi
 
 mainmenu_option next_comment
-comment 'Filesystems'
-
-tristate 'Standard (minix) fs support' CONFIG_MINIX_FS y
-bool 'Extended fs support' CONFIG_EXT_FS n
-bool 'Second extended fs support' CONFIG_EXT2_FS y
-tristate 'xiafs filesystem support' CONFIG_XIA_FS n
-tristate 'msdos fs support' CONFIG_MSDOS_FS y
-if [ "$CONFIG_MSDOS_FS" != "n" ]; then
-  tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
-fi
-bool '/proc filesystem support' CONFIG_PROC_FS y
-if [ "$CONFIG_INET" = "y" ]; then
-  tristate 'NFS filesystem support' CONFIG_NFS_FS y
-fi
-tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
-tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
-tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
-tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS n
-
-mainmenu_option next_comment
-comment 'character devices'
+comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
-bool 'Cyclades async mux support' CONFIG_CYCLADES n
-tristate 'Parallel printer support' CONFIG_PRINTER n
-bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
-bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
-if [ "$CONFIG_PSMOUSE" = "y" ]; then
-  bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
+bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI
+if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
+  source drivers/cdrom/Config.in
 fi
-bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
-bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
-
-
-bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
-if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
-  bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
-if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
-
-comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
 
-else
+source fs/Config.in
 
-comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
-comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
-
-fi
-fi
-
-bool 'QIC-117 tape support' CONFIG_FTAPE n
-if [ "$CONFIG_FTAPE" = "y" ]; then
-  int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
-fi
+source drivers/char/Config.in
 
 mainmenu_option next_comment
 comment 'Sound'
 
-bool 'Sound card support' CONFIG_SOUND n
+tristate 'Sound card support' CONFIG_SOUND
+if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
+  source drivers/sound/Config.in
+fi
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
-bool 'Kernel profiling support' CONFIG_PROFILE n
+#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
+bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
   int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
-fi
-if [ "$CONFIG_SCSI" = "y" ]; then
-bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
 fi
diff -u --recursive --new-file v1.3.35/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v1.3.35/linux/arch/alpha/defconfig	Thu Jan  1 02:00:00 1970
+++ linux/arch/alpha/defconfig	Sun Oct 22 22:17:47 1995
@@ -0,0 +1,170 @@
+#
+# Automatically generated make config: don't edit
+#
+
+#
+# General setup
+#
+CONFIG_NATIVE=y
+CONFIG_ALPHA_CABRIOLET=y
+# CONFIG_ALPHA_SRM is not set
+CONFIG_PCI=y
+CONFIG_ALPHA_APECS=y
+CONFIG_PCI_OPTIMIZE=y
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+CONFIG_BINFMT_ELF=y
+
+#
+# Loadable module support
+#
+# CONFIG_MODVERSIONS is not set
+
+#
+# block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_ST506=y
+
+#
+# Please see drivers/block/README.ide for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_TRITON is not set
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_XD is not set
+
+#
+# Networking options
+#
+# CONFIG_FIREWALL is not set
+CONFIG_INET=y
+# CONFIG_IP_FORWARD is not set
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_NET_IPIP is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_PCTCP is not set
+# CONFIG_INET_RARP is not set
+CONFIG_INET_SNARL=y
+# CONFIG_TCP_NAGLE_OFF is not set
+CONFIG_IP_NOSR=y
+CONFIG_SKB_LARGE=y
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_AX25 is not set
+# CONFIG_NETLINK is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CDrom)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_CHR_DEV_SG is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+CONFIG_SCSI_NCR53C7xx=y
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_QLOGIC is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_NCR53C406A is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_SLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SCC is not set
+# CONFIG_PLIP is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_ALPHA is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_APRICOT is not set
+CONFIG_DE4X5=y
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_TR is not set
+# CONFIG_ARCNET is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Filesystems
+#
+# CONFIG_MINIX_FS is not set
+# CONFIG_EXT_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_XIA_FS is not set
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_NFS_FS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_HPFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SMB_FS is not set
+
+#
+# character devices
+#
+# CONFIG_CYCLADES is not set
+# CONFIG_STALDRV is not set
+# CONFIG_PRINTER is not set
+# CONFIG_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PROFILE is not set
diff -u --recursive --new-file v1.3.35/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.35/linux/arch/i386/config.in	Mon Oct 16 18:38:21 1995
+++ linux/arch/i386/config.in	Sun Oct 22 22:16:56 1995
@@ -7,324 +7,78 @@
 mainmenu_option next_comment
 comment 'General setup'
 
-bool 'Kernel math emulation' CONFIG_MATH_EMULATION n
-tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
-bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y
-if [ "$CONFIG_ST506" = "y" ]; then
-  comment 'Please see drivers/block/README.ide for help/info on IDE drives'
-  bool '   Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD n
-  if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
-    bool '   Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n
-  else
-    bool '   Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y
-  fi
-  if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
-    bool '   Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD y
-  fi
-fi
-
-bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
-bool 'Networking support' CONFIG_NET y
-bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
-bool 'PCI bios support' CONFIG_PCI y
+bool 'Kernel math emulation' CONFIG_MATH_EMULATION
+bool 'Networking support' CONFIG_NET
+bool 'Limit memory to low 16MB' CONFIG_MAX_16M
+bool 'PCI bios support' CONFIG_PCI
 if [ "$CONFIG_PCI" = "y" ]; then
-  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE y
-  if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
-    bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON y
-  fi
+  bool '   PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE
 fi
-bool 'System V IPC' CONFIG_SYSVIPC y
-tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+bool 'System V IPC' CONFIG_SYSVIPC
+tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 if [ "$CONFIG_BINFMT_ELF" = "y" ]; then
-bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF y
+  bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF
 fi
-#bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586 n
+#bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586
 #if [ "$CONFIG_M586" = "n" ]; then
-bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
+bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486
 #fi
-bool 'SMP Kernel (experimental - gcc2.5.8 only: see Documentation/SMP.txt)' CONFIG_SMP n
+bool 'SMP Kernel (experimental - gcc2.5.8 only: see Documentation/SMP.txt)' CONFIG_SMP
 
 mainmenu_option next_comment
 comment 'Loadable module support'
-bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
+bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
+
+source drivers/block/Config.in
 
 if [ "$CONFIG_NET" = "y" ]; then
-mainmenu_option next_comment
-comment 'Networking options'
-bool 'TCP/IP networking' CONFIG_INET y
-if [ "$CONFIG_INET" = "y" ]; then
-bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n
-bool 'IP: multicasting' CONFIG_IP_MULTICAST n
-bool 'IP: firewalling' CONFIG_IP_FIREWALL n
-bool 'IP: accounting' CONFIG_IP_ACCT n
-tristate 'IP: tunneling' CONFIG_NET_IPIP n
-if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then
-  bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y
-  bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n
-fi
-if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_MULTICAST" = "y" ]; then
-  bool 'IP: multicast routing(in progress)' CONFIG_IP_MROUTE n
-fi
-comment '(it is safe to leave these untouched)'
-bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n
-tristate 'IP: Reverse ARP' CONFIG_INET_RARP n
-bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y
-bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
-bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y
-bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE y
-fi
-bool 'The IPX protocol' CONFIG_IPX n
-bool 'Appletalk DDP' CONFIG_ATALK n
-bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
-if [ "$CONFIG_AX25" = "y" ]; then
-  bool 'Amateur Radio NET/ROM' CONFIG_NETROM n
-fi
-bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK n
-if [ "$CONFIG_NETLINK" = "y" ]; then
-  bool 'Routing messages' CONFIG_RTNETLINK y
-fi
+  source net/Config.in
 fi
 
 mainmenu_option next_comment
 comment 'SCSI support'
 
-tristate 'SCSI support' CONFIG_SCSI n
-
-if [ "$CONFIG_SCSI" = "n" ]; then
-
-comment 'Skipping SCSI configuration options...'
-
-else
+tristate 'SCSI support' CONFIG_SCSI
 
-comment 'SCSI support type (disk, tape, CDrom)'
-
-dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD y $CONFIG_SCSI
-dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST n $CONFIG_SCSI
-dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR y $CONFIG_SCSI
-dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG n $CONFIG_SCSI
-
-comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
-
-bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n
-
-mainmenu_option next_comment
-comment 'SCSI low-level drivers'
-
-dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n $CONFIG_SCSI
-dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y $CONFIG_SCSI
-dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n $CONFIG_SCSI
-dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX n $CONFIG_SCSI
-dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n $CONFIG_SCSI
-dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA n $CONFIG_SCSI
-dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO n $CONFIG_SCSI
-dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n $CONFIG_SCSI
-dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n $CONFIG_SCSI
-bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n
-if [ "$CONFIG_PCI" = "y" ]; then
-  dep_tristate 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx n $CONFIG_SCSI
+if [ "$CONFIG_SCSI" != "n" ]; then
+  source drivers/scsi/Config.in
 fi
-dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n $CONFIG_SCSI
-bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n
-dep_tristate 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n $CONFIG_SCSI
-dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n $CONFIG_SCSI
-bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n
-dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n $CONFIG_SCSI
-dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST n $CONFIG_SCSI
-dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n $CONFIG_SCSI
-dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A n $CONFIG_SCSI
-#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n $CONFIG_SCSI
-fi
-
 
 if [ "$CONFIG_NET" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'Network device support'
 
-mainmenu_option next_comment
-comment 'Network device support'
-
-bool 'Network device support' CONFIG_NETDEVICES y
-if [ "$CONFIG_NETDEVICES" = "n" ]; then
-
-comment 'Skipping network driver configuration options...'
-
-else
-tristate 'Dummy net driver support' CONFIG_DUMMY m
-tristate 'SLIP (serial line) support' CONFIG_SLIP n
-if [ "$CONFIG_SLIP" != "n" ]; then
-  bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
-fi
-tristate 'PPP (point-to-point) support' CONFIG_PPP n
-if [ ! "$CONFIG_PPP" = "n" ]; then
-comment 'CCP compressors for PPP are only built as modules.'
-fi
-if [ "$CONFIG_AX25" = "y" ]; then
-	bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC n
-else
-	bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC n
-fi
-tristate 'PLIP (parallel port) support' CONFIG_PLIP n
-tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n
-bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
-bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
-if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
-	tristate 'WD80*3 support' CONFIG_WD80x3 n
-	tristate 'SMC Ultra support' CONFIG_ULTRA n
-fi
-bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n
-bool '3COM cards' CONFIG_NET_VENDOR_3COM y
-if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
-	tristate '3c501 support' CONFIG_EL1 n
-	tristate '3c503 support' CONFIG_EL2 n
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		tristate '3c505 support' CONFIG_ELPLUS n
-		tristate '3c507 support' CONFIG_EL16 n
-	fi
-	tristate '3c509/3c579 support' CONFIG_EL3 y
-fi
-bool 'Other ISA cards' CONFIG_NET_ISA n
-if [ "$CONFIG_NET_ISA" = "y" ]; then
-	tristate 'Cabletron E21xx support' CONFIG_E2100 n
-	tristate 'DEPCA support' CONFIG_DEPCA n
-	tristate 'EtherWorks 3 support' CONFIG_EWRK3 n
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		bool 'SEEQ8005 support' CONFIG_SEEQ8005 n
-		tristate 'AT1700 support' CONFIG_AT1700 n
-		tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n
-		tristate 'EtherExpress support' CONFIG_EEXPRESS n
-		bool 'NI5210 support' CONFIG_NI52 n
-		bool 'NI6510 support' CONFIG_NI65 n
-		tristate 'WaveLAN support' CONFIG_WAVELAN n
-	fi
-	tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n
-	tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n
-	tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 n
-	tristate 'NE2000/NE1000 support' CONFIG_NE2000 y
-	if [ "$CONFIG_AX25" = "y" ]; then
-		bool 'Ottawa PI and PI/2 support' CONFIG_PI y
-	fi
-	bool 'SK_G16 support' CONFIG_SK_G16 n
-fi
-bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA n
-if [ "$CONFIG_NET_EISA" = "y" ]; then
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		tristate 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
-	fi
-	tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
-	tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5 n
-#	tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP n
-#	bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n
-#	bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n
-	bool 'Zenith Z-Note support' CONFIG_ZNET y
-fi
-bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n
-if [ "$CONFIG_NET_POCKET" = "y" ]; then
-	bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
-	tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
-	tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n
-#	bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n
-#	bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n
-#	bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n
-fi
-bool 'Token Ring driver support' CONFIG_TR n
-if [ "$CONFIG_TR" = "y" ]; then
-	tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR y
-fi
-tristate 'Arcnet support' CONFIG_ARCNET n
-fi
-fi
-
-mainmenu_option next_comment
-comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
-
-bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI n
-if [ "$CONFIG_CD_NO_IDESCSI" = "y" ]; then
-  tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A n
-  tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD n
-  tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX n
-  tristate 'Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD n
-  if [ "$CONFIG_SBPCD" = "y" ]; then
-    bool 'Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2 n
-    if [ "$CONFIG_SBPCD2" = "y" ]; then
-      bool 'Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3 n
-      if [ "$CONFIG_SBPCD3" = "y" ]; then
-        bool 'Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4 n
-      fi
-    fi
+  bool 'Network device support' CONFIG_NETDEVICES
+  if [ "$CONFIG_NETDEVICES" = "y" ]; then
+    source drivers/net/Config.in
   fi
-  tristate 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
-  tristate 'Sony CDU535 CDROM support' CONFIG_CDU535 n
-  tristate 'Goldstar R420 CDROM support' CONFIG_GSCD n
-  tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206 n
-  tristate 'Experimental Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD n
-  tristate 'Experimental Sanyo H94A CDROM support' CONFIG_SJCD n
 fi
 
 mainmenu_option next_comment
-comment 'Filesystems'
-
-tristate 'Standard (minix) fs support' CONFIG_MINIX_FS y
-tristate 'Extended fs support' CONFIG_EXT_FS n
-tristate 'Second extended fs support' CONFIG_EXT2_FS y
-tristate 'xiafs filesystem support' CONFIG_XIA_FS n
-tristate 'msdos fs support' CONFIG_MSDOS_FS y
-if [ "$CONFIG_MSDOS_FS" != "n" ]; then
-  tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
-fi
-bool '/proc filesystem support' CONFIG_PROC_FS y
-if [ "$CONFIG_INET" = "y" ]; then
-  tristate 'NFS filesystem support' CONFIG_NFS_FS y
-fi
-tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
-tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
-tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
-tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS n
-
-mainmenu_option next_comment
-comment 'character devices'
+comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
-bool 'Cyclades async mux support' CONFIG_CYCLADES n
-bool 'Stallion multiport serial support' CONFIG_STALDRV n
-if [ "$CONFIG_STALDRV" = "y" ]; then
-  tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION n
-  tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION n
-fi
-tristate 'Parallel printer support' CONFIG_PRINTER n
-tristate 'Logitech busmouse support' CONFIG_BUSMOUSE n
-tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
-if [ "$CONFIG_PSMOUSE" = "y" ]; then
-  bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
+bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI
+if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
+  source drivers/cdrom/Config.in
 fi
-tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
-tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
-
 
-bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
-if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
-  bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
-if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+source fs/Config.in
 
-comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
-
-else
-
-comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
-comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
-
-fi
-fi
+source drivers/char/Config.in
 
 mainmenu_option next_comment
 comment 'Sound'
 
-tristate 'Sound card support' CONFIG_SOUND n
+tristate 'Sound card support' CONFIG_SOUND
+if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
+  source drivers/sound/Config.in
+fi
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
-bool 'Kernel profiling support' CONFIG_PROFILE n
+#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
+bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
   int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
-fi
-if [ "$CONFIG_SCSI" = "y" ]; then
-bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
 fi
diff -u --recursive --new-file v1.3.35/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.35/linux/arch/i386/defconfig	Thu Jan  1 02:00:00 1970
+++ linux/arch/i386/defconfig	Sun Oct 22 15:57:49 1995
@@ -0,0 +1,131 @@
+#
+# Automatically generated make config: don't edit
+#
+
+#
+# General setup
+#
+# CONFIG_MATH_EMULATION is not set
+CONFIG_NET=y
+# CONFIG_MAX_16M is not set
+CONFIG_PCI=y
+CONFIG_PCI_OPTIMIZE=y
+CONFIG_SYSVIPC=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+CONFIG_M486=y
+# CONFIG_SMP is not set
+
+#
+# Loadable module support
+#
+# CONFIG_MODVERSIONS is not set
+
+#
+# block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_ST506=y
+
+#
+# Please see drivers/block/README.ide for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDE=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_XD is not set
+
+#
+# Networking options
+#
+# CONFIG_FIREWALL is not set
+CONFIG_INET=y
+# CONFIG_IP_FORWARD is not set
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_NET_IPIP is not set
+
+#
+# (it is safe to leave these untouched)
+#
+# CONFIG_INET_PCTCP is not set
+# CONFIG_INET_RARP is not set
+CONFIG_INET_SNARL=y
+# CONFIG_TCP_NAGLE_OFF is not set
+CONFIG_IP_NOSR=y
+CONFIG_SKB_LARGE=y
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_AX25 is not set
+# CONFIG_NETLINK is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_SLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SCC is not set
+# CONFIG_PLIP is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_ALPHA is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_LANCE is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+CONFIG_EL3=y
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_TR is not set
+# CONFIG_ARCNET is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Filesystems
+#
+CONFIG_MINIX_FS=y
+# CONFIG_EXT_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_XIA_FS is not set
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_PROC_FS=y
+CONFIG_NFS_FS=y
+CONFIG_ISO9660_FS=y
+# CONFIG_HPFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SMB_FS is not set
+
+#
+# character devices
+#
+# CONFIG_CYCLADES is not set
+# CONFIG_STALDRV is not set
+# CONFIG_PRINTER is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_PSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PROFILE is not set
diff -u --recursive --new-file v1.3.35/linux/drivers/Makefile linux/drivers/Makefile
--- v1.3.35/linux/drivers/Makefile	Mon Oct 16 18:38:21 1995
+++ linux/drivers/Makefile	Sat Oct 21 17:50:51 1995
@@ -34,4 +34,9 @@
   endif
 endif
 
+ifdef CONFIG_CD_NO_IDESCSI
+SUB_DIRS += cdrom
+MOD_SUB_DIRS += cdrom
+endif
+
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.35/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v1.3.35/linux/drivers/block/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/Config.in	Sun Oct 22 22:16:56 1995
@@ -0,0 +1,23 @@
+#
+# Block device driver configuration
+#
+mainmenu_option next_comment
+comment 'block devices'
+
+tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
+bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506
+if [ "$CONFIG_ST506" = "y" ]; then
+  comment 'Please see drivers/block/README.ide for help/info on IDE drives'
+  bool '   Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD
+  if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
+    bool '   Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE
+  else
+    bool '   Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE
+  fi
+  if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+    bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON
+    bool '   Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD
+  fi
+fi
+
+bool 'XT harddisk support' CONFIG_BLK_DEV_XD
diff -u --recursive --new-file v1.3.35/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.3.35/linux/drivers/block/Makefile	Mon Oct 16 18:38:21 1995
+++ linux/drivers/block/Makefile	Sat Oct 21 17:52:56 1995
@@ -28,98 +28,6 @@
   endif
 endif
 
-ifeq ($(CONFIG_AZTCD),y)
-L_OBJS += aztcd.o
-else
-  ifeq ($(CONFIG_AZTCD),m)
-  M_OBJS += aztcd.o
-  endif
-endif #CONFIG_AZTCD
-
-ifeq ($(CONFIG_CDU31A),y)
-L_OBJS += cdu31a.o
-else
-  ifeq ($(CONFIG_CDU31A),m)
-  M_OBJS += cdu31a.o
-  endif
-endif #CONFIG_CDU31A
-
-ifeq ($(CONFIG_MCD),y)
-L_OBJS += mcd.o
-else
-  ifeq ($(CONFIG_MCD),m)
-  M_OBJS += mcd.o
-  endif
-endif #CONFIG_MCD
-
-ifeq ($(CONFIG_MCDX),y)
-L_OBJS += mcdx.o
-else
-  ifeq ($(CONFIG_MCDX),m)
-  M_OBJS += mcdx.o
-  endif
-endif #CONFIG_MCDX
-
-ifeq ($(CONFIG_SBPCD),y)
-L_OBJS += sbpcd.o
-else
-  ifeq ($(CONFIG_SBPCD),m)
-  M_OBJS += sbpcd.o
-  endif
-endif #CONFIG_SBPCD
-
-ifeq ($(CONFIG_SBPCD2),y)
-L_OBJS += sbpcd2.o
-endif #CONFIG_SBPCD2
-
-ifeq ($(CONFIG_SBPCD3),y)
-L_OBJS += sbpcd3.o
-endif #CONFIG_SBPCD3
-
-ifeq ($(CONFIG_SBPCD4),y)
-L_OBJS += sbpcd4.o
-endif #CONFIG_SBPCD4
-
-ifeq ($(CONFIG_CDU535),y)
-L_OBJS += sonycd535.o
-else
-  ifeq ($(CONFIG_CDU535),m)
-  M_OBJS += sonycd535.o
-  endif
-endif #CONFIG_CDU535
-
-ifeq ($(CONFIG_GSCD),y)
-L_OBJS += gscd.o
-else
-  ifeq ($(CONFIG_GSCD),m)
-  M_OBJS += gscd.o
-  endif
-endif #CONFIG_GSCD
-
-ifeq ($(CONFIG_CM206),y)
-L_OBJS += cm206.o
-else
-  ifeq ($(CONFIG_CM206),m)
-  M_OBJS += cm206.o
-  endif
-endif #CONFIG_CM206
-
-ifeq ($(CONFIG_OPTCD),y)
-L_OBJS += optcd.o
-else
-  ifeq ($(CONFIG_OPTCD),m)
-  M_OBJS += optcd.o
-  endif
-endif #CONFIG_OPTCD
-
-ifeq ($(CONFIG_SJCD),y)
-L_OBJS += sjcd.o
-else
-  ifeq ($(CONFIG_SJCD),m)
-  M_OBJS += sjcd.o
-  endif
-endif #CONFIG_SJCD
-
 ifeq ($(CONFIG_BLK_DEV_HD),y)
 L_OBJS += hd.o
 endif
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.aztcd linux/drivers/block/README.aztcd
--- v1.3.35/linux/drivers/block/README.aztcd	Mon Oct 16 18:38:21 1995
+++ linux/drivers/block/README.aztcd	Thu Jan  1 02:00:00 1970
@@ -1,793 +0,0 @@
-$Id: README.aztcd,v 1.80 1995/10/11 19:37:49 root Exp root $
-                       Readme-File README.aztcd
-   for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 
-                          CD-ROM  Driver 
-                       Version 1.7 and newer
-                   (for other drives see 6.-8.)
-
-NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE
-      A PROPRIETARY INTERFACE (implemented on a sound card or on a
-      ISA-AT-bus card). 
-      IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE,
-      such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE
-      WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE 
-      USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER !
-      THE STANDARD-KERNEL 1.2.x NOW ALSO SUPPORTS IDE-CDROM-DRIVES, SEE THE
-      HARDDISK (!) SECTION OF make config, WHEN COMPILING A NEW KERNEL!!!
-----------------------------------------------------------------------------
-
-Contents of this file:
-                         1.  NOTE
-                         2.  INSTALLATION
-                         3.  CONFIGURING YOUR KERNEL
-                         4.  RECOMPILING YOUR KERNEL
-                         4.1   AZTCD AS A RUN-TIME LOADABLE MODULE
-                         4.2   CDROM CONNECTED TO A SOUNDCARD
-                         5.  KNOWN PROBLEMS, FUTURE DEVELOPMENTS
-			 5.1   MULTISESSION SUPPORT
-			 5.2   STATUS RECOGNITION
-			 5.3   DOSEMU's CDROM SUPPORT
-                         6.  BUG REPORTS
-                         7.  OTHER DRIVES
-                         8.  IF YOU DON'T SUCCEED ... DEBUGGING  
-                         9.  TECHNICAL HISTORY OF THE DRIVER
-                        10.  ACKNOWLEDGMENTS
-                        11.  PROGRAMMING ADD ONS: CDPLAY.C
-                        APPENDIX: Source code of cdplay.c
-----------------------------------------------------------------------------
-
-1. NOTE 
-This software has been successfully in alpha and beta test for quite a long
-time with AZTECH CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110 
-and has proven to be stable with kernel versions 1.0.9 to 1.2.0. But with
-any software there still may be bugs in it. So if you encounter problems,
-you are invited to help us improve this software. Please send me a detailed
-bug report (see chapter BUG REPORTS). You are also invited in helping us to 
-increase the number of drives, which are supported.
-
-Please read the README-files carefully and always keep a backup copy of your 
-old kernel, in order to reboot if something goes wrong!
-
-
-2. INSTALLATION
-If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz 
-(xx=version number) and not included in the standard Linux kernel, read the 
-file AZTECH.CDROM.README included in that package to install it. The 
-standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'.
-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/block. It uses /dev/aztcd0, 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.
-
-
-3.  CONFIGURING YOUR KERNEL
-If your kernel is already configured for using the AZTECH driver you will
-see the following message while Linux boots:
-    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
-    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>>>
-    Aztech CD-ROM Init: <drive type> detected
-    Aztech CD-ROM Init: End
-If the message looks different and you are sure to have a supported drive,
-it may have a different base address. The Aztech driver does look for the 
-CD-ROM drive at the base address specified in aztcd.h at compile time. This 
-address can be overwritten by boot parameter aztcd=....You should reboot and 
-start Linux with boot parameter aztcd=<base address>, e.g. aztcd=0x320. If 
-you do not know the base address, start your PC with DOS and look at the boot 
-message of your CD-ROM's DOS driver. If that still does not help, use boot
-parameter aztcd=<base address>,0x79 , this tells aztcd to try a little harder.
-
-If the message looks correct, as user 'root' you should be able to mount the 
-drive by
-          mount -t iso9660 -r /dev/aztcd0 /mnt
-and use it as any other filesystem. (If this does not work, check if
-  /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
-      mknod /dev/aztcd0 b 29 0
-      mkdir /mnt                       
-
-If you still get a different message while Linux boots or when you get the 
-message, that the ISO9660-filesystem is not supported by your kernel, when
-you try to mount the CD-ROM drive, you have to recompile your kernel.
-
-If you do *not* have an Aztech/Orchid/Okano/Wearnes drive and want to bypass
-drive detection during Linux boot up, start with boot parameter aztcd=0.
-
-Joe Nardone has compiled a boot disk with the Aztech driver for installing 
-Slackware from CDROM. You can find the disk images at 'sunsite.unc.edu'; 
-see file 'aztech.gz.README' for instructions on how to use it.
-
-
-4. RECOMPILING YOUR KERNEL
-If your kernel is not yet configured for the AZTECH driver and the ISO9660-
-filesystem, you have to recompile your kernel: 
-
-- Edit aztcd.h to set the I/O-address to your I/O-Base address (AZT_BASE_ADDR), 
-  the driver does not use interrupts or DMA, so if you are using an AZTECH
-  CD268, an ORCHID CD-3110 or ORCHID/WEARNES CDD110 that's the only item you 
-  have to set up. If you have a soundcard, read chapter 4.2.
-  Users of other drives should read chapter OTHER DRIVES of this file.
-  You also can configure that address by LILO boot parameter aztcd=... 
-- There are some other points, which may be configured, e.g. auto-eject the
-  CD when umounting a drive, tray locking etc., see aztcd.h for details.
-- Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support' 
-  (if you want aztcd to be part of the kernel). Do not configure it for
-  'Aztech... support', if you want to use aztcd as a run time loadable module. 
-  But in any case you must have the ISO9660-filesystem included in your
-  kernel.
-- Activate the new kernel, normally this is done by running lilo (don't for-
-  get to configure it before and to keep a copy of your old kernel in case
-  something goes wrong!).
-- Reboot
-- If you've included aztcd in your kernel, you now should see during boot 
-  some messages like
-    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
-    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>
-    Aztech CD-ROM Init: <drive type> detected
-    Aztech CD-ROM Init: End
-- If you have not included aztcd in your kernel, but want to load aztcd as a 
-  run time loadable module see 4.1. 
-- If the message looks correct, as user 'root' you should be able to mount 
-  the drive by
-          mount -t iso9660 -r /dev/aztcd0 /mnt
-  and use it as any other filesystem. (If this does not work, check if
-  /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
-      mknod /dev/aztcd0 b 29 0
-      mkdir /mnt                       
-- If this still does not help, see chapters OTHER DRIVES and DEBUGGING.
-
-4.1 AZTCD AS A RUN-TIME LOADABLE MODULE
-If you do not need aztcd permanently, you can also load and remove the driver
-during runtime via insmod and rmmod. To build aztcd as a loadable module you 
-must *not* configure your kernel for AZTECH support. Nevertheless, you may
-run into problems, if the version of your boot kernel is not the same than
-the source kernel version, from which 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. There are
-also some special features which may be configured, e.g. auto-eject a CD
-when unmounting the drive etc; see aztcd.h for details. Then change
-to /usr/src/linux and do a 
-                    make modules  
-	            make modules_install
-After that you can run-time load the driver via
-                    insmod /lib/modules/X.X.X/misc/aztcd.o
-and remove it via   rmmod  aztcd.
-If you did not set the correct base address in aztcd.h, you can also supply the
-base address when loading the driver via
-                    insmod /lib/modules/X.X.X/misc/aztcd.o aztcd=<base address>
-If you do not have the iso9660-filesystem in your boot kernel, you also have
-to load it before you can mount the CDROM:
-                    insmod /lib/modules/X.X.X/fs/isofs.o
-The mount procedure works as described in 4. above.
-(In all commands 'X.X.X' is the current linux kernel version number. For details
-see file README.modules in /usr/src/linux.)
-
-4.2 CDROM CONNECTED TO A SOUNDCARD
-Most soundcards do have a bus interface to the CDROM-drive. In many cases
-this soundcard needs to be configured, before the CDROM can be used. This
-configuration procedure consists of writing some kind of initialization
-data to the soundcard registers. The AZTECH-CDROM driver in the moment does
-only support one type of soundcard (SoundWave32). Users of other soundcards
-should try to boot DOS first and let their DOS drivers initialize the
-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,
-uncomment line '#define AZT_SW32' and set the appropiate value for
-AZT_BASE_ADDR and AZT_SW32_BASE_ADDR. This support was tested with an Orchid
-CDS-3110 connected to a SoundWave32.
-If you want your soundcard to be supported, find out, how it needs to be
-configured and mail me (see 6.) the appropriate information. 
-
-5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
-5.1 MULTISESSION SUPPORT
-Multisession support for CD's still is a myth. I implemented and tested a basic
-support for multisession and XA CDs, but I still have not enough CDs and appli-
-cations to test it rigourously. So if you'd like to help me, please contact me
-(Email address see below). As of version 1.4 and newer you can enable the 
-multisession support in aztcd.h by setting AZT_MULTISESSION to 1. Doing so 
-will cause the ISO9660-filesystem to deal with multisession CDs, ie. redirect 
-requests to the Table of Contents (TOC) information from the last session, 
-which contains the info of all previous sessions etc.. If you do set 
-AZT_MULTISESSION to 0, you can use multisession CDs anyway. In that case the 
-drive's firmware will do automatic redirection. For the ISO9660-filesystem any 
-multisession CD  will then look like a 'normal' single session CD. But never-
-theless the data of all sessions is viewable and accessible. So with practical-
-ly all real world applications you won't notice the difference. But as future
-applications may make use of advanced multisession features, I've started to
-implement the interface for the ISO9660 multisession interface via ioctl
-CDROMMULTISESSION.
-
-5.2 STATUS RECOGNITION
-The drive status recognition does not work correctly in all cases. Changing
-a disk or having the door open, when a drive is already mounted, is detected 
-by the Aztech driver itself, but nevertheless causes multiple read attempts
-by the different layers of the ISO9660-filesystem driver, which finally timeout,
-so you have to wait quite a little... But isn't it bad style to change a disk 
-in a mounted drive, anyhow ?!
-
-The driver uses busy wait in most cases for the drive handshake (macros
-STEN_LOW and DTEN_LOW). I tested with a 486/DX2 at 66MHz and a Pentium at
-60MHz. Whenever you use a much faster machine you are likely to get timeout 
-messages. In that case edit aztcd.h and increase the timeout value AZT_TIMEOUT. 
-
-For some 'slow' drive commands I implemented waiting with a timer waitqueue
-(macro STEN_LOW_WAIT). If you get this timeout message, you may also edit
-aztcd.h and increase the timeout value AZT_STATUS_DELAY. The waitqueue has
-shown to be a little critical. If you get kernel panic messages, edit aztcd.c
-and substitute STEN_LOW_WAIT by STEN_LOW. Busy waiting with STEN_LOW is more
-stable, but also causes CPU overhead.
-
-5.3 DOSEMU's CD-ROM SUPPORT
-With release 1.20 aztcd was modified to allow access to CD-ROMS when running
-under dosemu-0.60. aztcd-versions before 1.20 are most likely to crash
-Linux, when a CD-ROM is accessed under dosemu. This problem has partly been
-fixed, but still when accessing a directory for the first time the system
-might hang for some 30sec. So be patient, when using dosemu's CD-ROM support
-in combination with aztcd :-) ! Unfortunately up to now, I could not locate
-the root cause of that problem. It seems to be somewhere in the interaction
-of the kernel with dosemu's and aztcd's buffers. I appreciate any help on
-this subject ! 
-This problem has now (July 1995) been fixed by a modification to dosemu's
-CD-ROM driver, but it is unclear, with which version of dosemu this modi-
-fication will officially be included.
-
-6. BUG REPORTS
-Please send detailed bug reports and bug fixes via EMail to
-
-        zimmerma@rz.fht-esslingen.de
-
-Please include a description of your CD-ROM drive type and interface card, 
-the exact firmware message during Linux bootup, the version number of the 
-AZTECH-CDROM-driver and the Linux kernel version. Also a description of your 
-system's other hardware could be of interest, especially microprocessor type, 
-clock frequency, other interface cards such as soundcards, ethernet adapter, 
-game cards etc..
-
-I will try to collect the reports and make the necessary modifications from 
-time to time. I may also come back to you directly with some bug fixes and 
-ask you to do further testing and debugging.
-
-Editors of CD-ROMs are invited to send a 'cooperation' copy of their
-CD-ROMs to the volunteers, who provided the CD-ROM support for Linux. My
-snail mail address for such 'stuff' is
-           Prof. Dr. W. Zimmermann
-           Fachhochschule fuer Technik Esslingen
-           Fachbereich NT
-           Flandernstrasse 101
-           D-73732 Esslingen
-           Germany
-
-
-7. OTHER DRIVES
-The following drives ORCHID CDS3110, OKANO CDD110 and WEARNES nearly look 
-the same as AZTECH CDA268-01A, especially they seem to use the same command 
-codes. So it was quite simple to make the AZTECH driver work with these drives. 
-
-Unfortunately I do not have any of these drives available, so I couldn't test
-it myself. But I've got reports, that it works with ORCHID CDS3110 and Game-
-Wave32 sound cards and also with WEARNES CDD110 in some different combinations. 
-In some installations, it seems necessary to initialize the drive with the DOS 
-driver before (especially if combined with a sound card) and then do a warm 
-boot (CTRL-ALT-RESET) or start Linux from DOS, e.g. with 'loadlin'.
-
-If you do not succeed, read chapter DEBUGGING. Thanks in advance!
-
-Sorry for the inconvenience, but it is difficult to develop for hardware, 
-which you don't have available for testing. So if you like, please help us.
-
-
-8. DEBUGGING : IF YOU DON'T SUCCEED, TRY THE FOLLOWING
--reread the complete README file
--make sure, that your drive is hardware configured for 
-    transfer mode: polled
-    IRQ:           not used
-    DMA:           not used
-    Base Address:  something like 300, 320 ...
- You can check this, when you start the DOS driver, which came with your
- drive. By appropriately configuring the drive and the DOS driver you can
- check, whether your drive does operate in this mode correctly under DOS. If
- it does not operate under DOS, it won't under Linux.
- Make sure the Base Address is configured correctly in aztcd.h, also make
- sure, that /dev/aztcd0 exists with the correct major number (compare it with
- the entry in file /usr/include/linux/major.h for the Aztech drive). 
--insert a CD-ROM and close the tray
--cold boot your PC (i.e. via the power on switch or the reset button)
--if you start Linux via DOS, e.g. using loadlin, make sure, that the DOS
- driver for the CD-ROM drive is not loaded (comment out the calling lines 
- in DOS' config.sys!)
--look for the aztcd: init message during Linux init and note them exactly
--log in as root and do a mount -t iso9660 /dev/aztcd0 /mnt
--if you don't succeed in the first time, try several times. Try also to open
- and close the tray, then mount again. Please note carefully all commands
- you typed in and the aztcd-messages, which you get.
--if you get an 'Aztech CD-ROM init: aborted' message, read the remarks about
- the version string below.
-
-If this does not help, do the same with the following differences 
--start DOS before; make now sure, that the DOS driver for the CD-ROM is 
- loaded under DOS (i.e. uncomment it again in config.sys)
--warm boot your PC (i.e. via CTRL-ALT-DEL)
- if you have it, you can also start via loadlin (try both).
- ...
- Again note all commands and the aztcd-messages.
-
-If you see STEN_LOW or STEN_LOW_WAIT error messages, increase the timeout
-values.
-
-If this still does not help, 
--look in aztcd.c for the lines  #if 0
-                                #define AZT_TEST1
-                                ...
-                                #endif
- and substitute '#if 0' by '#if 1'.
--recompile your kernel and repeat the above two procedures. You will now get 
- a bundle of debugging messages from the driver. Again note your commands
- and the appropriate messages. If you have syslogd running, these messages
- may also be found in syslogd's kernel log file. Nevertheless in some
- installations syslogd does not yet run, when init() is called, thus look for
- the aztcd-messages during init, before the login-prompt appears.
- Then look in aztcd.c, to find out, what happened. The normal calling sequence 
- is: aztcd_init() during Linux bootup procedure init()
- after doing a 'mount -t iso9660 /dev/aztcd0 /mnt' the normal calling sequence is
-     aztcd_open()    -> Status 2c after cold reboot with CDROM or audio CD inserted
-                     -> Status 8  after warm reboot with CDROM inserted          
-                     -> Status 2e after cold reboot with no disk, closed tray
-                     -> Status 6e after cold reboot, mount with door open
-     aztUpdateToc()
-     aztGetDiskInfo()
-     aztGetQChannelInfo()   repeated several times
-     aztGetToc()
-     aztGetQChannelInfo()   repeated several times
-     a list of track informations
-     do_aztcd_request()  }  
-     azt_transfer()    } repeated several times
-     azt_poll          }
- Check, if there is a difference in the calling sequence or the status flags!
- 
- There are a lot of other messages, eg. the ACMD-command code (defined in
- aztcd.h), status info from the getAztStatus-command and the state sequence of
- the finite state machine in azt_poll(). The most important are the status
- messages, look how they are defined and try to understand, if they make
- sense in the context where they appear. With a CD-ROM inserted the status 
- should always be 8, except in aztcd_open(). Try to open the tray, insert a
- audio disk, insert no disk or reinsert the CD-ROM and check, if the status
- bits change accordingly. The status bits are the most likely point, where 
- the drive manufacturers may implement changes.
-            
-If you still don't succeed, a good point to start is to look in aztcd.c in 
-function aztcd_init, where the drive should be detected during init. Do the
-following:
--reboot the system with boot parameter 'aztcd=<your base address>,0x79'. With
- parameter 0x79 most of the drive version detection is bypassed. After that 
- you should see the complete version string including leading and trailing 
- blanks during init. 
- Now adapt the statement
-      if ((result[1]=='A')&&(result[2]=='Z' ...)
- in aztcd_init() to exactly match the first 3 or 4 letters you have seen.
--Another point is the 'smart' card detection feature in aztcd_init(). Normally
- the CD-ROM drive is ready, when aztcd_init is trying to read the version
- string and a time consuming ACMD_SOFT_RESET command can be avoided. This is
- detected by looking, if AFL_OP_OK can be read correctly. If the CD-ROM drive 
- hangs in some unknown state, e.g. because of an error before a warm start or 
- because you first operated under DOS, even the version string may be correct, 
- but the following commands will not. Then change the code in such a way, 
- that the ACMD_SOFT_RESET is issued in any case, by substituting the
- if-statement 'if ( ...=AFL_OP_OK)' by 'if (1)'.
-
-If you succeed, please mail may the exact version string of your drive and
-the code modifications, you have made together with a short explanation.
-If you don't succeed, you may mail me the output of the debugging messages.
-But remember, they are only useful, if they are exact and complete and you
-describe in detail your hardware setup and what you did (cold/warm reboot,
-with/without DOS, DOS-driver started/not started, which Linux-commands etc.)
-
-
-9. TECHNICAL HISTORY OF THE DRIVER
-The AZTECH-Driver is a rework of the Mitsumi-Driver. Four major items had to
-be reworked:
-
-a) The Mitsumi drive does issue complete status information acknowledging
-each command, the Aztech drive does only signal that the command was
-processed. So whenever the complete status information is needed, an extra
-ACMD_GET_STATUS command is issued. The handshake procedure for the drive
-can be found in the functions aztSendCmd(), sendAztCmd() and getAztStatus().
-
-b) The Aztech Drive does not have a ACMD_GET_DISK_INFO command, so the
-necessary info about the number of tracks (firstTrack, lastTrack), disk
-length etc. has to be read from the TOC in the lead in track (see function
-aztGetDiskInfo()).
-
-c) Whenever data is read from the drive, the Mitsumi drive is started with a
-command to read an indefinite (0xffffff) number of sectors. When the appropriate 
-number of sectors is read, the drive is stopped by a ACDM_STOP command. This
-does not work with the Aztech drive. I did not find a way to stop it. The
-stop and pause commands do only work in AUDIO mode but not in DATA mode.
-Therefore I had to modify the 'finite state machine' in function azt_poll to
-only read a certain number of sectors and then start a new read on demand. As I 
-have not completely understood, how the buffer/caching scheme of the Mitsumi 
-driver was implemented, I am not sure, if I have covered all cases correctly, 
-whenever you get timeout messages, the bug is most likely to be in that
-function azt_poll() around switch(cmd) .... case ACD_S_DATA. 
-
-d) I did not get information about changing drive mode. So I doubt, that the
-code around function azt_poll() case AZT_S_MODE does work. In my test I have
-not been able to switch to reading in raw mode. For reading raw mode, Aztech
-uses a different command than for cooked mode, which I only have implemen-
-ted in the ioctl-section but not in the section which is used by the ISO9660- 
-
-The driver was developed on an AST PC with Intel 486/DX2, 8MB RAM, 340MB IDE 
-hard disk and on an AST PC with Intel Pentium 60MHz, 16MB RAM, 520MB IDE 
-running Linux kernel version 1.0.9 from the LST 1.8 Distribution. The kernel 
-was compiled with gcc.2.5.8. My CD-ROM drive is an Aztech CDA268-01A. My
-drive says, that it has Firmware Version AZT26801A1.3. It came with a ISA-bus
-interface card and works with polled I/O without DMA and without interrupts.
-The code for all other drives was 'remote' tested and debugged by a number of 
-volunteers on the Internet.
-
-Points, where I feel that possible problems might be and all points where I 
-did not completely understand the drive's behaviour or trust my own code are 
-marked with /*???*/ in the source code. There are also some parts in the 
-Mitsumi driver, where I did not completely understand their code.
-
-
-10. ACKNOWLEDGMENTS
-Without the help of P.Bush, Aztech, who delivered technical information
-about the Aztech Drive and without the help of E.Moenkeberg, GWDG, who did a
-great job in analyzing the command structure of various CD-ROM drives, this
-work would not have been possible. E.Moenkeberg was also a great help in 
-making the software 'kernel ready' and in answering many of the CDROM-related 
-questions in the newsgroups. He really is *the* Linux CD-ROM guru. Thanks 
-also to all the guys on the Internet, who collected valuable technical 
-information about CDROMs. 
-
-Joe Nardone (joe@access.digex.net) was a patient tester even for my first
-trial, which was more than slow, and made suggestions for code improvement.
-Especially the 'finite state machine' azt_poll() was rewritten by Joe to get
-clean C code and avoid the ugly 'gotos', which I copied from mcd.c.
-
-Robby Schirmer (schirmer@fmi.uni-passau.de) tested the audio stuff (ioctls) 
-and suggested a lot of patches for them.
-
-Joseph Piskor and Peter Nugent were the first users with the ORCHID CD3110
-and also were very patient with the problems which occurred.
-
-Reinhard Max delivered the information for the CDROM-interface of the
-SoundWave32 soundcards.
-
-Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
-directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'.
-
-11. PROGRAMMING ADD ONs: cdplay.c
-You can use the ioctl-functions included in aztcd.c in your own programs. As
-an example on how to do this, you will find a tiny CD Player for audio CDs 
-named 'cdplay.c'. It allows you to play audio CDs. You can play a specified 
-track, pause and resume or skip tracks forward and backwards. If you quit the 
-program without stopping the  drive, playing is continued. You can also 
-(mis)use cdplay to read and hexdump data disks.
-'cdplay.c' can be found as a separate file in package AZTECH.CDROM.Vxx.tgz.
-If you don't have it, you can find the code in the APPENDIX of this file,
-which you should cut out with an editor and store in a separate file
-'cdplay.c'. To compile it and make it executable, do
-  gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # compiles it
-  chmod +755 /usr/local/bin/cdplay                              # makes it executable
-  ln -s /dev/aztcd0 /dev/cdrom                                  # creates a link
-   (for /usr/lib substitute the top level directory, where your include files 
-    reside,  and for /usr/local/bin the directory, where you want the executable 
-    binary to reside )
-
-You have to set the correct permissions for cdplay *and* for /dev/mcd0 or
-/dev/aztcd0 in order to use it. Remember, that you should not have /dev/cdrom 
-mounted, when you're playing audio CDs. 
-
-This program is just a hack for testing the ioctl-functions in aztcd.c, I will 
-not maintain it, so if you run into problems, discard it or have a look into 
-the source code 'cdplay.c'. The program does only contain a minimum of user 
-protection and input error detection. If you use the commands in the wrong 
-order or if you try to read a CD at wrong addresses, you may get error messages
-or even hang your machine. If you get STEN_LOW, STEN_LOW_WAIT or segment violation 
-error messages when using cdplay, after that, the system might not be stable 
-any more, so you'd better reboot. As the ioctl-functions run in kernel mode,
-most normal Linux-multitasking protection features do not work. By using
-uninitialized 'wild' pointers etc., it is easy to write to other users data and
-program areas, destroy kernel tables etc.. So if you experiment with ioctls
-as always when you are doing systems programming and kernel hacking, you
-should have a backup copy of your system in a safe place (and you also
-should try before, how to restore from a backup copy)!
-
-
-Werner Zimmermann
-Fachhochschule fuer Technik Esslingen
-(EMail: zimmerma@rz.fht-esslingen.de)
-July 26, 1995
-
----------------------------------------------------------------------------
-APPENDIX: Source code of cdplay.c
-
-/* Tiny Audio CD Player
-
-   Copyright 1994, 1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
-
-This program originally was written to test the audio functions of the
-AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before 
-using it, you should set a symlink from /dev/cdrom to your real CDROM
-device.
-
-The GNU General Public License applies to this program.
-
-History:  V0.1  W.Zimmermann: First release. Nov. 8, 1994
-          V0.2  W.Zimmermann: Enhanced functionality. Nov. 9, 1994
-          V0.3  W.Zimmermann: Additional functions. Nov. 28, 1994          
-          V0.4  W.Zimmermann: fixed some bugs. Dec. 17, 1994
-          V0.5  W.Zimmermann: clean 'scanf' commands without compiler warnings
-                              Jan. 6, 1995
-          V0.6  W.Zimmermann: volume control (still experimental). Jan. 24, 1995
-          V0.7  W.Zimmermann: read raw modified. July 26, 95
-*/
-
-#include <stdio.h>
-#include <ctype.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <linux/cdrom.h>
-#include <linux/aztcd.h>
-
-void help(void)
-{ printf("Available Commands:  STOP         s      EJECT/CLOSE  e       QUIT         q\n");
-  printf("                     PLAY TRACK   t      PAUSE        p       RESUME       r\n");
-  printf("                     NEXT TRACK   n      REPEAT LAST  l       HELP         h\n");
-  printf("                     SUB CHANNEL  c      TRACK INFO   i       PLAY AT      a\n");
-  printf("                     READ         d      READ RAW     w       VOLUME       v\n");
-}
-
-int main(void)
-{ int handle;
-  unsigned char command=' ', ini=0, first=1, last=1;
-  unsigned int cmd, i,j,k, arg1,arg2,arg3;
-  struct cdrom_ti       ti;
-  struct cdrom_tochdr   tocHdr;
-  struct cdrom_subchnl  subchnl;
-  struct cdrom_tocentry entry;
-  struct cdrom_msf      msf;
-  union  { struct cdrom_msf msf;
-           unsigned char buf[CD_FRAMESIZE_RAW];
-         } azt;
-  struct cdrom_volctrl  volctrl;
-
-  printf("\nMini-Audio CD-Player V0.71   (C) 1994,1995  W.Zimmermann\n");
-  handle=open("/dev/cdrom",O_RDWR);
-  ioctl(handle,CDROMRESUME);
-  
-  if (handle<=0) 
-    { printf("Drive Error: already playing, no audio disk, door open\n");
-      printf("             or no permission (you must be ROOT in order to use this program)\n");
-    }
-  else
-    { help();
-      while (1)
-        { printf("Type command (h = help):  ");
-          scanf("%s",&command); 
-          switch (command)
-            { case 'e':   cmd=CDROMEJECT;
-                          ioctl(handle,cmd);
-                          break;  
-              case 'p':   if (!ini)
-                             { printf("Command not allowed - play track first\n");
-                             }
-                          else
-                             { cmd=CDROMPAUSE;
-                               if (ioctl(handle,cmd)) printf("Drive Error\n");
-                             }
-                          break;
-              case 'r':   if (!ini)
-                             { printf("Command not allowed - play track first\n");
-                             }
-                          else
-                             { cmd=CDROMRESUME;
-                               if (ioctl(handle,cmd)) printf("Drive Error\n");
-                             }
-                          break;
-              case 's':   cmd=CDROMPAUSE;
-                          if (ioctl(handle,cmd)) printf("Drive error or already stopped\n");
-                          cmd=CDROMSTOP;
-                          if (ioctl(handle,cmd)) printf("Drive error\n");
-                          break;
-              case 't':   cmd=CDROMREADTOCHDR;
-                          if (ioctl(handle,cmd,&tocHdr)) printf("Drive Error\n");
-                          first=tocHdr.cdth_trk0;
-                          last= tocHdr.cdth_trk1;
-                          if ((first==0)||(first>last))
-                            { printf ("--could not read TOC\n");
-                            }
-                          else
-                            { printf("--first track: %d   --last track: %d   --enter track number: ",first,last);
-                              cmd=CDROMPLAYTRKIND;
-                              scanf("%i",&arg1);
-                              ti.cdti_trk0=arg1;
-                              if (ti.cdti_trk0<first) ti.cdti_trk0=first;
-                              if (ti.cdti_trk0>last)  ti.cdti_trk0=last;
-                              ti.cdti_ind0=0;
-                              ti.cdti_trk1=last;
-                              ti.cdti_ind1=0;
-                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
-                              ini=1;
-                            } 
-                          break;
-              case 'n':   if (!ini++) 
-                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
-                              first=tocHdr.cdth_trk0;
-                              last= tocHdr.cdth_trk1;
-                              ti.cdti_trk0=first-1;
-                            }
-                          if ((first==0)||(first>last))
-                            { printf ("--could not read TOC\n");
-                            }
-                          else
-                            { cmd=CDROMPLAYTRKIND;
-                              if (++ti.cdti_trk0 > last)  ti.cdti_trk0=last;
-                              ti.cdti_ind0=0;
-                              ti.cdti_trk1=last;
-                              ti.cdti_ind1=0;
-                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
-                              ini=1;
-                            }
-                          break;
-              case 'l':   if (!ini++)
-                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
-                              first=tocHdr.cdth_trk0;
-                              last= tocHdr.cdth_trk1;
-                              ti.cdti_trk0=first+1;
-                            }
-                          if ((first==0)||(first>last))
-                            { printf ("--could not read TOC\n");
-                            }
-                          else
-                            { cmd=CDROMPLAYTRKIND;
-                              if (--ti.cdti_trk0 < first) ti.cdti_trk0=first;
-                              ti.cdti_ind0=0;
-                              ti.cdti_trk1=last;
-                              ti.cdti_ind1=0;
-                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
-                              ini=1;
-                            }  
-                          break;
-              case 'c':   subchnl.cdsc_format=CDROM_MSF;
-                          if (ioctl(handle,CDROMSUBCHNL,&subchnl)) 
-                            printf("Drive Error\n");
-                          else
-                            { printf("AudioStatus:%s   Track:%d  Mode:%d   MSF=%d:%d:%d\n", \
-                              subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",\
-                              subchnl.cdsc_trk,subchnl.cdsc_adr, \
-                              subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, \
-                              subchnl.cdsc_absaddr.msf.frame);
-                            }
-                          break;              
-              case 'i':   if (!ini)
-                            { printf("Command not allowed - play track first\n");
-                            }
-                          else
-                            { cmd=CDROMREADTOCENTRY;
-                              printf("Track No.: ");
-                              scanf("%d",&arg1);
-                              entry.cdte_track=arg1;
-                              if (entry.cdte_track<first) entry.cdte_track=first;
-                              if (entry.cdte_track>last)  entry.cdte_track=last;
-			      entry.cdte_format=CDROM_MSF;
-                              if (ioctl(handle,cmd,&entry)) 
-                               { printf("Drive error or invalid track no.\n");
-                               }
-                              else
-                               { printf("Mode %d Track, starts at %d:%d:%d\n", \
-                               entry.cdte_adr,entry.cdte_addr.msf.minute, \
-                               entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame);
-                               }
-                            }
-                          break;
-              case 'a':   cmd=CDROMPLAYMSF;
-                          printf("Address (min:sec:frame)  ");
-                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
-                          msf.cdmsf_min0  =arg1;
-                          msf.cdmsf_sec0  =arg2;
-                          msf.cdmsf_frame0=arg3;
-                          if (msf.cdmsf_sec0  > 59) msf.cdmsf_sec0  =59;
-                          if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74;
-                          msf.cdmsf_min1=60;
-                          msf.cdmsf_sec1=00;
-                          msf.cdmsf_frame1=00;
-                          if (ioctl(handle,cmd,&msf)) 
-                           { printf("Drive error or invalid address\n");
-                           }
-                          break;
-#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
-              case 'd':   cmd=CDROMREADCOOKED;
-                          printf("Address (min:sec:frame)  ");
-                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
-                          azt.msf.cdmsf_min0  =arg1;
-                          azt.msf.cdmsf_sec0  =arg2;
-                          azt.msf.cdmsf_frame0=arg3;
-                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
-                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
-                          if (ioctl(handle,cmd,&azt.msf)) 
-                           { printf("Drive error, invalid address or unsupported command\n");
-                           }
-                          k=0;
-                          getchar();
-                          for (i=0;i<128;i++)
-                           { printf("%4d:",i*16);
-                             for (j=0;j<16;j++)
-                               { printf("%2x ",azt.buf[i*16+j]);
-                               }
-                             for (j=0;j<16;j++)
-                               { if (isalnum(azt.buf[i*16+j])) 
-                                   printf("%c",azt.buf[i*16+j]);
-                                 else
-                                   printf(".");
-                               }
-                             printf("\n"); 
-                             k++;
-                             if (k>=20)
-                              { printf("press ENTER to continue\n");
-                                getchar();
-                                k=0;
-                              }
-                           } 
-                          break;
-              case 'w':   cmd=CDROMREADRAW;
-                          printf("Address (min:sec:frame)  ");
-                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
-                          azt.msf.cdmsf_min0  =arg1;
-                          azt.msf.cdmsf_sec0  =arg2;
-                          azt.msf.cdmsf_frame0=arg3;                          
-                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
-                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
-                          if (ioctl(handle,cmd,&azt)) 
-                           { printf("Drive error, invalid address or unsupported command\n");
-                           }
-                          k=0;
-                          for (i=0;i<147;i++)
-                           { printf("%4d:",i*16);
-                             for (j=0;j<16;j++)
-                               { printf("%2x ",azt.buf[i*16+j]);
-                               }
-                             for (j=0;j<16;j++)
-                               { if (isalnum(azt.buf[i*16+j])) 
-                                   printf("%c",azt.buf[i*16+j]);
-                                 else
-                                   printf(".");
-                               }
-                             printf("\n"); 
-                             k++;
-                             if (k>=20)
-                              { getchar();
-                                k=0;
-                              }
-                           } 
-                          break;
-#endif
-              case 'v':   cmd=CDROMVOLCTRL;
-                          printf("--Channel 0 Left  (0-255): ");
-                          scanf("%d",&arg1);
-                          printf("--Channel 1 Right (0-255): ");
-                          scanf("%d",&arg2);
-                          volctrl.channel0=arg1;
-                          volctrl.channel1=arg2;
-                          volctrl.channel2=0;
-                          volctrl.channel3=0;
-                          if (ioctl(handle,cmd,&volctrl)) 
-                           { printf("Drive error or unsupported command\n");
-                           }
-                          break;  
-              case 'q':   if (close(handle)) printf("Drive Error: CLOSE\n");
-                          exit(0);
-              case 'h':   help();
-                          break;
-              default:    printf("unknown command\n");
-                          break;
-            }
-       }
-    }
-  return 0;
-}
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.cdu31a linux/drivers/block/README.cdu31a
--- v1.3.35/linux/drivers/block/README.cdu31a	Fri Jul  7 08:54:44 1995
+++ linux/drivers/block/README.cdu31a	Thu Jan  1 02:00:00 1970
@@ -1,160 +0,0 @@
-   Tips for using cdu31a.c, the driver for Sony CDU-31A and CDU-33A CDROM
-   drives.
-  
-   Corey Minyard (minyard@wf-rch.cirr.com)
-  
-   Colossians 3:17
-  
-   The Sony interface device driver handles Sony interface CDROM
-   drives and provides a complete block-level interface as well as an
-   ioctl() interface compatible with the Sun (as specified in
-   include/linux/cdrom.h).  With this interface, CDROMs can be
-   accessed and standard audio CDs can be played back normally.
-  
-   WARNING - 	All autoprobes have been removed from the driver.
-  		You MUST configure the CDU31A via a LILO config
-  		at boot time or in lilo.conf.  I have the
-  		following in my lilo.conf:
-  
-                  append="cdu31a=0x1f88,0,PAS"
-  
-  		The first number is the I/O base address of the
-  		card.  The second is the interrupt (0 means none).
-  		The third should be "PAS" if on a Pro-Audio
-  		spectrum, or nothing if on something else.
-  
-   This interface is (unfortunately) a polled interface.  This is
-   because most Sony interfaces are set up with DMA and interrupts
-   disables.  Some (like mine) do not even have the capability to
-   handle interrupts or DMA.  For this reason you will see a lot of
-   the following:
-  
-     retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
-     while ((retry_count > jiffies) && (! <some condition to wait for))
-     {
-        while (handle_sony_cd_attention())
-           ;
-  
-        sony_sleep();
-     }
-     if (the condition not met)
-     {
-        return an error;
-     }
-  
-   This ugly hack waits for something to happen, sleeping a little
-   between every try.  it also handles attentions, which are
-   asynchronous events from the drive informing the driver that a disk
-   has been inserted, removed, etc.
-  
-   NEWS FLASH - The driver now supports interrupts but they are
-   turned off by default.  Use of interrupts is highly encouraged, it
-   cuts CPU usage down to a reasonable level.  I had DMA in for a while
-   but PC DMA is just too slow.  Better to just insb() it.
-  
-   One thing about these drives: They talk in MSF (Minute Second Frame) format.
-   There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
-   disk.  The funny thing is that these are sent to the drive in BCD, but the
-   interface wants to see them in decimal.  A lot of conversion goes on.
-  
-   DRIVER SPECIAL FEATURES
-   -----------------------
-  
-   This section describes features beyond the normal audio and CD-ROM
-   functions of the drive.
-  
-   2048 byte buffer mode
-  
-   If a disk is mounted with -o block=2048, data is copied straight
-   from the drive data port to the buffer.  Otherwise, the readahead
-   buffer must be involved to hold the other 1K of data when a 1K
-   block operation is done.  Note that with 2048 byte blocks you
-   cannot execute files from the CD.
-  
-   XA compatibility
-  
-   The driver should support XA disks for both the CDU31A and CDU33A.
-   It does this transparently, the using program doesn't need to set it.
-  
-   Multi-Session
-  
-   A multi-session disk looks just like a normal disk to the user.
-   Just mount one normally, and all the data should be there.
-   A special thanks to Koen for help with this!
-   
-   Raw sector I/O
-  
-   Using the CDROMREADAUDIO it is possible to read raw audio and data
-   tracks.  Both operations return 2352 bytes per sector.  On the data
-   tracks, the first 12 bytes is not returned by the drive and the value
-   of that data is indeterminate.
-  
-  
-    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.
-  
-  /
-
-/*
-  
-   Setting up the Sony CDU31A/CDU33A drive interface card.  If
-   You have another card, you are on your own.
-   
-        +----------+-----------------+----------------------+
-        |  JP1     |  34 Pin Conn    |                      |
-        |  JP2     +-----------------+                      |
-        |  JP3                                              |
-        |  JP4                                              |
-        |                                                   +--+
-        |                                                   |  +-+
-        |                                                   |  | |  External
-        |                                                   |  | |  Connector
-        |                                                   |  | |
-        |                                                   |  +-+
-        |                                                   +--+
-        |                                                   |
-        |                                          +--------+
-        |                                          |
-        +------------------------------------------+
-   
-      JP1 sets the Base Address, using the following settings:
-   
-        Address         Pin 1           Pin 2
-        -------         -----           -----
-        0x320           Short           Short
-        0x330           Short           Open
-        0x340           Open            Short
-        0x360           Open            Open
-   
-      JP2 and JP3 configure the DMA channel; they must be set the same.
-   
-        DMA             Pin 1           Pin 2           Pin 3
-        ---             -----           -----           -----
-        1               On              Off             On
-        2               Off             On              Off
-        3               Off             Off             On
-   
-      JP4 Configures the IRQ:
-   
-        IRQ     Pin 1           Pin 2           Pin 3           Pin 4
-        ---     -----           -----           -----           -----
-        3       Off             Off             On              Off
-        4       Off             Off*            Off             On
-        5       On              Off             Off             Off
-        6       Off             On              Off             Off
-   
-                  The documentation states to set this for interrupt
-                  4, but I think that is a mistake.
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.cm206 linux/drivers/block/README.cm206
--- v1.3.35/linux/drivers/block/README.cm206	Tue Jul 25 18:21:20 1995
+++ linux/drivers/block/README.cm206	Thu Jan  1 02:00:00 1970
@@ -1,177 +0,0 @@
-This is the readme file for the driver for the Philips/LMS cdrom drive
-cm206 in combination with the cm260 host adapter card. 
-
-				(c) 1995 David A. van Leeuwen
-   
-Features as of version 0.33
----------------------------
-- Full audio support, that is, both  workman, workbone and cdp work
-  now reasonably. Reading TOC still takes some time. xmcd has been
-  reported to run successfully. 
-- Made auto-probe code a little better, i hope
-
-Features as of version 0.28
----------------------------
-- Full speed stransfer rate (300 kB/s).
-- Minimum kernel memory usage for buffering (less than 3 kB).
-- Multisession support.
-- Tray locking.
-- Statistcics of driver accessible to the user.
-- Module support.
-- Auto-probing of adapter card's base port and irq line,
-  also configurable at boot time or module load time.
-
-Features still lacking
-----------------------
-- cm205ms+cm250 support. (I do have cm205ms docs now. I still have to
-  study Kai Petzke's cm205 drives to understand the workings of the
-  cm250 adapter card. Don't bet on me, write a driver yourself!)
-
-
-Decide how you are going to use the driver. There are two
-options:
-
-   (a) installing the driver as a resident part of the kernel
-   (b) compiling the driver as a loadable module
-
-   Further, you must decide if you are going to specify the base port
-   address and the interrupt request line of the adapter card cm260 as
-   boot options for (a), module parameters for (b), use automatic
-   probing of these values, or hard-wire your adaptor cards settings
-   into the source code. If you don't care, you can choose for
-   autoprobing, which is the default. In that case you can move on to
-   the next step.
-
-Compiling the kernel
---------------------
-1) move to /usr/src/linux and do a 
-
-	make config
-
-   If you have chosen for option (a), answer yes to CONFIG_CM206 and
-   CONFIG_ISO9660_FS.
-
-   If you have chosen for option (b), answer yes to CONFIG_MODVERSIONS
-   and no (!) to CONFIG_CM206 and CONFIG_ISO9660_FS. 
-
-2) then do a 
-	
-	make dep; make clean; make zImage; make modules
-
-3) do the usual things to install a new image (backup the old one, run
-   `rdev -R zImage 1', copy the new image in place, run lilo).  Might
-   be `make zlilo'.
-
-Using the driver as a module
-----------------------------
-If you will only seldomly use the cd-rom driver, you can choose for
-option (b), install as a loadable module. You may have to re-compile
-the module when you upgrade the kernel to a new version. Read the file
-`README.modules' in /usr/src/linux. To install the module, you use the 
-command, as root
-
-	insmod /usr/src/linux/modules/cm206.o
-
-You can specify the base address on the command line as well as the irq 
-line to be used, e.g.
-
-	insmod /usr/src/linux/modules/cm206.o cm206=0x300,11
-
-The order of base port and irq line don't matter; you may specify only
-one, the other will have the value of the compiled-in default.  You
-may also have to install the file-system module `iso9660.o', if you
-didn't compile that into the kernel. (If you use `tcsh' as shell, you
-might consider defining
-
-	alias listinstalledmodules 'cat /proc/modules | awk \{print\$1\}'
-	complete rmmod		'p/1/`listinstalledmodules`/'
-	alias listcompiledmodules '(cd /usr/src/linux/modules; \ls -o *.o)'
-	alias insmod		'insmod /usr/src/linux/modules/\!:1 \!:2*'
-	complete insmod		'p/1/`listcompiledmodules`/'
-
-which makes typing insmod and rmmod somewhat easier.)
-
-Using the driver as part of the kernel
---------------------------------------
-If you have chosen for option a, you can specify the base-port
-address and irq on the lilo boot command line, e.g.:
-
-	LILO: linux cm206=0x340,11
-
-This assumes that your linux kernel image keyword is `linux'. 
-If you may specify either IRQ (3--11) or base port (0x300--0x370),
-auto probing is turned off for both settings, thus setting the 
-other value to the compiled-in default.
-
-If module parameters and LILO config options don't work
--------------------------------------------------------
-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
-the defines of CM206_IRQ and CM206_BASE.
-
-
-
-Mounting the cdrom
-------------------
-1) Make sure that there is the right device installed in /dev.
-
-  	mknod /dev/cm206cd b 32 0
-
-2) Make sure there is a mount point, e.g., /cdrom 
-
-	mkdir /cdrom
-
-3) mount using a command like this (run as root):
-
-	mount -rt iso9660 /dev/cm206cd /cdrom
-
-4) For user-mounts, add a line in /etc/fstab
-
-	/dev/cm206cd      /cdrom     iso9660 	ro,noauto,user
-
-   This will allow users to give the commands
-
-	mount /cdrom
-	umount /cdrom
-
-If things don't work
---------------------
-
-- Try to do a `tail /var/adm/messages' to find out if the driver
-  said anything about what is going wrong during the initialization.
-
-- Try to do a `dd if=/dev/cm206cd | od -tc | less' to read from the
-  CD.
-
-- Look in the /proc directory to see if `cm206' shows up under
-  one of `interrupts', `ioports', `devices' or `modules' (if
-  applicable). 
-
-
-DISCLAIMER 
----------- 
-I cannot guarantee that this driver works, or that the hardware will
-not be harmed, although i consider it most unlikely. 
-
-I hope that you'll find this driver in some way useful. 
-
-					David van Leeuwen
-					david@tm.tno.nl
-
-Note for Linux CDROM vendors
------------------------------
-You are encouraged to include this driver on your Linux CDROM. If
-you do, you might consider sending me a free copy of that cd-rom.
-You can contact me through my e-mail address, david@tm.tno.nl. 
-If this driver is compiled into a kernel to boot off a cdrom, 
-you should actually send me a free copy of that cd-rom. 
-
-Copyright
----------
-The copyright of the cm206 driver for Linux is 
-
-    (c) 1995 David A. van Leeuwen
-
-The driver is released, like most Linux software, under the conditions 
-of the GNU general public license.
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.gscd linux/drivers/block/README.gscd
--- v1.3.35/linux/drivers/block/README.gscd	Fri Jul  7 08:54:44 1995
+++ linux/drivers/block/README.gscd	Thu Jan  1 02:00:00 1970
@@ -1,60 +0,0 @@
-	Goldstar R420 CD-Rom device driver README
-
-For all kind of other information about the GoldStar R420 CDROM
-and this Linux device driver is a WWW-URL Page installed:
-
-        http://linux.rz.fh-hannover.de/~raupach        
-
-
-      If you are the editor of a Linux CD, you should
-      enable gscd.c within your boot floppy kernel. Please,
-      send me one of your CDs for free.
-
-
-This current driver version 0.4a only supports reading data from the disk.
-Currently we have no audio and no multisession or XA support.
-The polling interface is used, no DMA.
-
-
-Sometimes the GoldStar R420 is sold in a 'Reveal Multimedia Kit'. This kit's
-drive interface is compatible, too.
-
-
-Installation
-------------
-
-Change to '/usr/src/linux/include/linux' 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. 
-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             
-
-Then go back to '/usr/src/linux/' and 'make config' to build the new
-configuration for your kernel. If you want to use the GoldStar driver
-like a module, don't select 'GoldStar CDROM support'. By the way, you
-have to include the iso9660 filesystem.
-
-Now start compiling the kernel with 'make dep ; make zImage'.
-If you want to use the driver as a module, you have to do 'make modules' 
-and 'make modules_install', additionally.
-Install your new kernel as usual - maybe you do it with 'make zlilo'.
-
-Before you can use the driver, you have to
-   mknod /dev/gscd0 b 16 0
-to create the appropriate device file (once for all times).
-
-If you use modules, you can try to insert the driver.
-Say: 'insmod /usr/src/linux/modules/gscd.o'
-or:  'insmod /usr/src/linux/modules/gscd.o gscd=<address>'
-The driver should report his results now.
-
-That's it! Mount a disk, i.e. 'mount -rt iso9660 /dev/gscd0 /cdrom'
-
-Feel free to report errors and suggestions to the following address.
-Be sure, I'm very happy to receive your comments!
- 
-        Oliver Raupach                                Hannover, Juni 1995
-(raupach@nwfs1.rz.fh-hannover.de)
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.3.35/linux/drivers/block/README.ide	Mon Sep 18 14:53:48 1995
+++ linux/drivers/block/README.ide	Fri Oct 20 10:10:39 1995
@@ -36,6 +36,8 @@
 		- this support also enables the secondary i/f on most cards
 NEW!	- support for secondary interface on the FGI/Holtek HT-6560B VLB i/f
 		- use kernel command line option:   ide1=ht6560
+NEW!	- experimental "fast" speed support for QD6580 interfaces
+		- use kernel command line option:   ide0=qd6580
 NEW!	- experimental support for DTC-2278D interfaces
 		- use kernel command line option:   ide1=dtc2278
 NEW!	- support for drives with a stuck WRERR_STAT bit
@@ -53,6 +55,10 @@
 
 For work in progress, see the comments in ide.c, ide-cd.c, and triton.c.
 
+Note that there is now a group actively working on support for the Promise
+caching IDE cards, such as the DC4030VL, and early results are encouraging.
+Look for this support to be added to the kernel soon.
+
 
 ***  IMPORTANT NOTICES (for kernel versions prior to 1.3.22)
 ***  =================
@@ -212,28 +218,33 @@
 
   "hdx="  is recognized for all "x" from "a" to "h", such as "hdc".
   "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
-
-  "hdx=noprobe"		: drive may be present, but do not probe for it
-  "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
-  "hdx=cdrom"		: drive is present, and is a cdrom drive
-  "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
-
-  "idex=noprobe"	: do not attempt to access/use this interface
-  "idex=base"		: probe for an interface at the addr specified,
-                                where "base" is usually 0x1f0 or 0x170
-                                and "ctl" is assumed to be "base"+0x206
-				and "irq" will be probed for
-  "idex=base,ctl"	: specify both base and ctl
-  "idex=base,ctl,irq"	: specify base, ctl, and irq number
-
-The following two are valid ONLY on ide0 or ide1:
-
-  "idex=dtc2278"	: look for and try to initialize a dtc2278
-  "idex=serialize"	: do not overlap operations on ide0 and ide1.
-  "idex=ht6560b"        : enables use of HT6560B secondary i/f
-  "idex=cmd640_vlb"     : required for VLB cards with the CMD640 chip
-                           (not for PCI versions)
-
+ 
+  "hdx=noprobe"        : drive may be present, but do not probe for it
+  "hdx=nowerr"         : ignore the WRERR_STAT bit on this drive
+  "hdx=cdrom"          : drive is present, and is a cdrom drive
+  "hdx=cyl,head,sect"  : disk drive is present, with specified geometry
+ 
+  "idex=noprobe"       : do not attempt to access/use this interface
+  "idex=base"          : probe for an interface at the addr specified,
+                               where "base" is usually 0x1f0 or 0x170
+                               and "ctl" is assumed to be "base"+0x206
+  "idex=base,ctl"      : specify both base and ctl
+  "idex=base,ctl,irq"  : specify base, ctl, and irq number
+ 
+  The following two are valid ONLY on ide0 or ide1,
+  and the defaults for the base,ctl ports must not be altered.
+ 
+  "idex=serialize"     : do not overlap operations on ide0 and ide1.
+  "idex=dtc2278"       : enables use of DTC2278 secondary i/f
+  "idex=ht6560b"       : enables use of HT6560B secondary i/f
+  "idex=cmd640_vlb"    : required for VLB cards with the CMD640 chip
+                         (not for PCI -- automatically detected)
+ 
+  This option is valid ONLY on ide0, and the defaults for the base,ctl ports 
+  must not be altered.
+  
+  "ide0=qd6580"        : select "fast" interface speed on a qd6580 interface
+ 
 Everything else is rejected with a "BAD OPTION" message.
 
 ================================================================================
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.mcd linux/drivers/block/README.mcd
--- v1.3.35/linux/drivers/block/README.mcd	Sun Sep  3 12:26:50 1995
+++ linux/drivers/block/README.mcd	Thu Jan  1 02:00:00 1970
@@ -1,4 +0,0 @@
-This driver does not support XA or MultiSession CDs (PhotoCDs). Use the
-experimental driver mcdx.c for that.
-
-You can use mcd for one interface, and mcdx for another.
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.mcdx linux/drivers/block/README.mcdx
--- v1.3.35/linux/drivers/block/README.mcdx	Sun Sep  3 12:26:50 1995
+++ linux/drivers/block/README.mcdx	Thu Jan  1 02:00:00 1970
@@ -1,44 +0,0 @@
-This is a first attempt to create an `improved' driver for the Mitsumi drives.
-It is able to "live together" with mcd.c, if you have at least two Mitsumi
-drives: each driver can use his own drive.
-
-To allow this "coexistence" as long as mcdx.c is not a superset of mcd.c,
-this driver has to use its own device files. We use MAJOR 20 for it. So,
-you have to do
-
- # mknod /dev/mcdx0 b 20 0
- # mknod /dev/mcdx1 b 20 1
-
-and so on, one entry for each drive to support, once.
-
-If you are using the driver as a module, you can specify your ports and IRQs
-like
-
- # insmod mcdx.o mcdx=0x300,11,0x304,5
-
-and so on ("address,IRQ" pairs).
-This will override the configuration in mcdx.h.
-
-This driver:
-
-	o	handles XA (and hopefully) multi session CDs as well as
-		ordinary CDs;
-	o	supports up to 5 drives (of course, you'll need free 
-		IRQs, i/o ports and slots);
-	o	uses much less kernel memory than the standard mcd driver
-		(no extra driver internal buffers!).
-    o   plays audio (like the `old' driver, I hope)
-
-This version doesn't support yet:
-
-	o	shared IRQs (but it seems to be possible - I've successfully
-                connected two drives to the same irq.  So it's `only' a 
-                problem of the driver.)
-
-This driver never will:
-
-	o	Read digital audio (i.e. copy directly), due to missing
-		hardware features. 
-
-
-heiko@lotte.sax.de
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.optcd linux/drivers/block/README.optcd
--- v1.3.35/linux/drivers/block/README.optcd	Mon Aug 28 14:52:18 1995
+++ linux/drivers/block/README.optcd	Thu Jan  1 02:00:00 1970
@@ -1,46 +0,0 @@
-This is the README file for the Optics Storage 8000 AT CDROM device driver.
-
-The driver contains code to enable an ISP16 interface if it finds one.  It
-didn't originally (although this README erroneously said so), because I think
-this kind of code should go into its own module. But having to use a hack all
-the time in order to use a part of the standard kernel started to annoy me, so
-I copied the ISP16 code by Eric van der Maarel (maarel@marin.nl) from Vadim
-Model's Sanyo sjcd driver. I'll remove it again from this driver when we have
-some common way to talk to ISP16 interfaces.
-
-My original configuration code for the ISP-16 card can get found at
-      dutette.et.tudelft.nl:/pub/linux/
-and at Eberhard's mirror
-      ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
-
-Much more elaborate information can be found at ftp:rbrf.msk.su,
-where Vadim Model (vadim@rbrf.msk.su) has made available an ISP16
-device driver.
-Vadim's directory is
-      rbrf.msk.su:/linux/mediamagic/
-and Eberhard is holding a mirror at
-      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
-
-
-Before you can use the driver, you have to create the device file once:
- # mknod /dev/optcd0 b 17 0
-
-To specify the base address if the driver is "compiled-in" to your kernel,
-you can use the kernel command line item (LILO option)
-             optcd=0x340
-with the right address.
-
-If you have compiled optcd as a module, you can load it with
- # insmod /usr/src/linux/modules/optcd.o
-or
- # insmod /usr/src/linux/modules/optcd.o optcd=0x340
-with the matching address value of your interface card.
-
-I have tried the module with several 1.2.x kernel versions, and it seems to
-work, as far as I tested. It also seems to work for several 1.3.x versions.
-If you use it, I'd appreciate success/failure reports. If you find a bug,
-try recompiling the driver with some strategically chosen #undef DEBUG_...'s
-changed into #defines (you'll find them in .../include/linux/optcd.h) and
-include the messages generated in your bug report. Good luck.
-
-Leo Spiekman (spiekman@dutette.et.tudelft.nl)
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd
--- v1.3.35/linux/drivers/block/README.sbpcd	Fri Sep 15 11:12:59 1995
+++ linux/drivers/block/README.sbpcd	Thu Jan  1 02:00:00 1970
@@ -1,1049 +0,0 @@
-This README belongs to release 3.9 or newer of the SoundBlaster Pro
-(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC)
-CD-ROM driver for Linux.
-
-Sbpcd really, really is NOT for ANY IDE/ATAPI drive!
-Not even if you have an "original" SoundBlaster card with an IDE interface!
-So, you better have a look into README.ide if your port address is 0x1F0,
-0x170, 0x1E8, 0x168 or similar.
-I get tons of mails from IDE/ATAPI drive users - I really can't continue
-any more to answer them all. So, if your drive/interface information sheets
-mention "IDE" (primary, secondary, tertiary, quaternary) and the DOS driver
-invoking line within your CONFIG.SYS is using an address below 0x230:
-DON'T ROB MY LAST NERVE - jumper your interface to address 0x170 and IRQ 15
-(that is the "secondary IDE" configuration), set your drive to "master" and
-use ide-cd as your driver. If you do not have a second IDE hard disk, use the
-LILO commands
-   hdb=noprobe hdc=cdrom
-and get lucky.
-To make it fully clear to you: if you mail me about IDE/ATAPI drive problems,
-my answer is above, and I simply will discard your mail, hoping to stop the
-flood and to find time to lead my 12-years old son towards happy computing.
-
-The driver is able to drive the whole family of "traditional" AT-style (that
-is NOT the new "Enhanced IDE" or "ATAPI" drive standard) Matsushita,
-Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The
-well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
-CR-574 is an IDE/ATAPI drive.
-
-The Longshine LCS-7260 is a double-speed drive which uses the "old"
-Matsushita command set. It is supported - with help by Serge Robyns.
-
-There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563
-with a special controller board. This drive is supported (the interface is
-of the "LaserMate" type), and it is possibly the best buy today (cheaper than
-an internal drive, and you can use it as an internal, too - f.e. plug it into
-a soundcard).
-
-CreativeLabs has a new drive "CD200" and a similar drive "CD200F". The latter
-is made by Funai and sometimes named "E2550UA". Support is under construction
- - CD200F should work, CD200 is still giving problems.
-Drive detection and playing audio should work. I need qualified feedback
-about the bugs within the data functions or a drive (I never saw a CD200).
-
-The quad-speed TEAC CD-55A drive is supported, but still does not reach "full
-speed". The data rate already reaches 500 kB/sec if you set SBP_BUFFER_FRAMES
-to 64 (it is not recommended to do that for normal "file access" usage, but it
-can speed up things a lot if you use something like "dd" to read from the
-drive; I use it for verifying self-written CDs this way).
-The drive itself is able to deliver 600 kB/sec, so this has to get a point of
-work; with the normal setup, the performance currently is not even as good as
-double-speed.
-
-This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives,
-and again: this driver is in no way usable for any IDE/ATAPI drive. If you 
-think your drive should work and it doesn't: send me the DOS driver for your
-beast (gzipped + uuencoded) and your CONFIG.SYS if you want to ask me for help,
-and include an original log message excerpt, and try to give all information
-a complete idiot needs to understand your hassle already with your first
-mail. And if you want to say "as I have mailed you before", be sure that I
-don't remember your "case" by such remarks; at the moment, I have some 
-hundreds open correspondences about Linux CDROM questions (hope to reduce if
-the IDE/ATAPI user questions disappear). 
-
-
-This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
-SoundFX, Mozart, ...) and with the "no-sound" cards (Panasonic CI-101P,
-LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...).
-
-It finally works now with the "configurable" interface "Sequoia S-1000", too,
-which is found on the Spea Media FX and Ensonic Soundscape sound cards. You
-have to specify the type "SBPRO 2" and the true CDROM port address with it,
-not the "configuration port" address.
-
-If you have a sound card which needs a "configuration driver" instead of
-jumpers for interface types and addresses (like Mozart cards) - those
-drivers get invoked before the DOS CDROM driver in your CONFIG.SYS, typical
-names are "cdsetup.sys" and "mztinit.sys" -, let the sound driver do the
-CDROM port configuration (the leading comments within 
-linux/drivers/sound/mad16.c are just for you!). Hannu Savolainen's mad16.c
-code is able to set up my Mozart card - I simply had to add
-   #define MAD16_CONF 0x06
-   #define MAD16_CDSEL 0x03
-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, 
-because the register layout is different between the "SoundBlaster" and the
-"LaserMate" type.
-
-I got a report that the TEAC interface card "I/F E117098" is of type
-"SoundBlaster" (i.e. you have to set SBPRO to 1) even with the addresses
-0x300 and above. This is unusual, and it can't get covered by the auto
-probing scheme.
-If auto-probing found the drive, the address is correct. The reported type
-may be wrong. A "mount" will give success only if the interface type is set
-right. Playing audio should work with a wrong set interface type, too.
-
-With some TEAC and some CD200 drives I have seen interface cards which seem
-to lack the "drive select" lines; always drive 0 gets addressed. To avoid
-"mirror drives" (four drives detected where you only have one) with such
-interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0 (if
-possible).
-
-
-Up to 4 drives per interface card, and up to 4 interface cards are supported.
-All supported drive families can be mixed, but the CR-521 drives are 
-hard-wired to drive ID 0. The drives have to use different drive IDs, and each
-drive has to get a unique minor number (0...3), corresponding indirectly to 
-its drive ID.
-The drive IDs may be selected freely from 0 to 3 - they do not have to be in
-consecutive order.
-
-As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible
-to change old drives to any ID, too. He writes in this sense:
-   "In order to be able to use more than one single speed drive
-   (they do not have the ID jumpers) you must add a DIP switch
-   and two resistors. The pads are already on the board next to
-   the power connector. You will see the silkscreen for the
-   switch if you remove the top cover.
-                    1 2 3 4
-             ID 0 = x F F x             O = "on"
-             ID 1 = x O F x             F = "off"
-             ID 2 = x F O x             x = "don't care"
-             ID 3 = x O O x
-   Next to the switch are the positions for R76 (7k) and R78
-   (12k). I had to play around with the resistor values - ID 3
-   did not work with other values. If the values are not good,
-   ID 3 behaves like ID 0."
-
-To use more than 4 drives, you simply need a second controller card at a 
-different address and a second cable.
-
-The driver supports reading of data from the CD and playing of audio tracks.
-The audio part should run with WorkMan, xcdplayer, with the "non-X11" products
-CDplayer and WorkBone - tell me if it is not compatible with other software.
-
-With the CR-562 and CR-563 drives, the reading of audio frames is possible.
-This is implemented by an IOCTL function which reads READ_AUDIO frames of
-2352 bytes at once (configurable with the "READ_AUDIO" define, default is 0).
-Reading the same frame a second time gives different data; the frame data 
-start at a different position, but all read bytes are valid, and we always
-read 98 consecutive chunks (of 24 Bytes) as a frame. Reading more than 1 frame
-at once possibly misses some chunks at each frame boundary. This lack has to
-get corrected by external, "higher level" software which reads the same frame 
-again and tries to find and eliminate overlapping chunks (24-byte-pieces).
-
-The transfer rate with reading audio (1-frame-pieces) currently is very slow.
-This can be better reading bigger chunks, but the "missing" chunks possibly
-occur at the beginning of each single frame.
-The software interface possibly may change a bit the day the SCSI driver
-supports it too.
-
-With all but the CR-52x drives, MultiSession is supported.
-Photo CDs work (the "old" drives like CR-521 can access only the first
-session of a photoCD).
-At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to
-convert photo CD image files and Gerd Knorr's viewing utility.
-
-The transfer rate will reach 150 kB/sec with CR-52x drives, 300 kB/sec with
-CR-56x drives, and currently not more than 500 kB/sec (usually less than
-250 kB/sec) with the TEAC quad speed drives.
-XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
-
-This release consists of
-- this README file
-- the driver file linux/drivers/block/sbpcd.c
-- the stub files linux/drivers/block/sbpcd[234].c
-- the header file linux/include/linux/sbpcd.h.
-
-
-To install:
------------
-
-1. Setup your hardware parameters. Though the driver does "auto-probing" at a
-   lot of (not all possible!) addresses, this step is recommended for
-   every-day use. You should let sbpcd auto-probe once and use the reported
-   address if a drive got found. The reported type may be incorrect; it is
-   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
-      hardware (near the beginning):
-      a1. Set it up for the appropriate type of interface board.
-          "Original" CreativeLabs sound cards need "SBPRO 1".
-          Most "compatible" sound cards (almost all "non-CreativeLabs" cards)
-          need "SBPRO 0".
-          The "no-sound" board from OmniCd needs the "SBPRO 1" setup.
-          All other "no-sound" boards need the "SBPRO 0" setup.
-          Possibly some TEAC "no-sound" boards need the "SBPRO 1" setup.
-          The Spea Media FX sound card needs "SBPRO 2".
-          sbpcd.c holds some examples in its auto-probe list.
-          If you configure "SBPRO" wrong, the playing of audio CDs will work,
-          but you will not be able to mount a data CD.
-      a2. Tell the address of your CDROM_PORT (not of the sound port).
-      a3. If 4 drives get found, but you have only one, set MAX_DRIVES to 1.
-      a4. Set DISTRIBUTION to 0.
-   b. Additionally for 2.a1 and 2.a2, the setup may be done during
-      boot time (via the "kernel command line" or "LILO option"):
-          sbpcd=0x230,SoundBlaster
-      or
-          sbpcd=0x320,LaserMate
-      or
-          sbpcd=0x330,SPEA
-      This is especially useful if you install a fresh distribution.
-      If the second parameter is a number, it gets taken as the type
-      setting; 0 is "LaserMate", 1 is "SoundBlaster".
-      So, for example
-          sbpcd=0x230,1
-      is equivalent to
-          sbpcd=0x230,SoundBlaster
-
-2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita
-   CD-ROM support and for ISO9660 FileSystem support. If you do not have a
-   second, third, or fourth controller installed, do not say "y" to the 
-   secondary Matsushita CD-ROM questions.
-
-3. Then do a "make dep", then make the kernel image ("make zlilo" or else).
-
-4. Make the device file(s). This step usually already has been done by the
-   MAKEDEV script.
-   The driver uses MAJOR 25, so, if necessary, do
-        mknod /dev/sbpcd  b 25 0       (if you have only one drive)
-   and/or
-        mknod /dev/sbpcd0 b 25 0
-        mknod /dev/sbpcd1 b 25 1
-        mknod /dev/sbpcd2 b 25 2
-        mknod /dev/sbpcd3 b 25 3
-   to make the node(s).
-
-   The "first found" drive gets MINOR 0 (regardless to its jumpered ID), the
-   "next found" (at the same cable) gets MINOR 1, ...
-   
-   For a second interface board, you have to make nodes like
-        mknod /dev/sbpcd4 b 26 0
-        mknod /dev/sbpcd5 b 26 1
-   and so on. Use the MAJORs 26, 27, 28.
-
-   If you further make a link like
-        ln -s sbpcd /dev/cdrom
-   you can use the name /dev/cdrom, too.
-
-5. Reboot with the new kernel.
-
-You should now be able to do
-              mkdir /CD
-and 
-              mount -rt iso9660 /dev/sbpcd /CD
-or
-              mount -rt iso9660 -o block=2048 /dev/sbpcd /CD
-and see the contents of your CD in the /CD directory.
-To use audio CDs, a mounting is not recommended (and it would fail if the
-first track is not a data track).
-
-
-Using sbpcd as a "loadable module":
------------------------------------
-
-If you do NOT select "Matsushita/Panasonic CDROM driver support" during the
-"make config" of your kernel, you can build the "loadable module" sbpcd.o.
-Read /usr/src/linux/README.modules on this.
-
-If sbpcd gets used as a module, the support of more than one interface
-card (i.e. drives 4...15) is disabled.
-
-You can specify interface address and type with the "insmod" command like:
- # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x340,0
-or
- # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1
-where the last number represents the SBPRO setting (no strings allowed here).
-
-
-Things of interest:
--------------------
-
-The driver is configured to try the LaserMate type of interface at I/O port
-0x0340 first. If this is not appropriate, sbpcd.h should get changed
-(you will find the right place - just at the beginning).
-
-No DMA and no IRQ is used.
-
-To reduce or increase the amount of kernel messages, edit sbpcd.c and play
-with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug").
-Don't forget to reflect what you do; enabling all DBG_xxx switches at once
-may crash your system.
-
-The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to
-specify "block=2048" as a mount option. Doing this will disable the direct
-execution of a binary from the CD; you have to copy it to a device with the
-standard BLOCK_SIZE (1024) before. So, do not use this if your system is
-directly "running from the CDROM" (like some of YGGDRASIL's installation
-variants). There are CDs on the market (like the german "unifix" Linux
-distribution) which MUST get handled with a block_size of 1024. Generally,
-one can say all the CDs which hold files of the name YMTRANS.TBL are defective;
-do not use block=2048 with those.
-
-Within sbpcd.h, you will find some "#define"s (f.e. EJECT and JUKEBOX). With
-that, you can configure the driver for some special things.
-You can use the appended program "cdtester" to set the auto-eject feature
-during runtime. Jeff Tranter's "eject" utility can do this, too (and more)
-for you.
-
-There is an ioctl CDROMMULTISESSION to obtain with a user program if
-the CD is an XA disk and - if it is - where the last session starts. The
-"cdtester" program illustrates how to call it.
-
-
-Auto-probing at boot time:
---------------------------
-
-The driver does auto-probing at many well-known interface card addresses,
-but not all:
-Some probings can cause a hang if an NE2000 ethernet card gets touched, because
-SBPCD's auto-probing happens before the initialization of the net drivers.
-Those "hazardous" addresses are excluded from auto-probing; the "kernel 
-command line" feature has to be used during installation if you have your 
-drive at those addresses. The "module" version is allowed to probe at those
-addresses, too.
-
-The auto-probing looks first at the configured address resp. the address
-submitted by the kernel command line. With this, it is possible to use this
-driver within installation boot floppies, and for any non-standard address,
-too.
-
-Auto-probing will make an assumption about the interface type ("SBPRO" or not),
-based upon the address. That assumption may be wrong (initialization will be
-o.k., but you will get I/O errors during mount). In that case, use the "kernel
-command line" feature and specify address & type at boot time to find out the
-right setup.
-
-For every-day use, address and type should get configured within sbpcd.h. That
-will stop the auto-probing due to success with the first try.
-
-The kernel command "sbpcd=0" suppresses each auto-probing and causes
-the driver not to find any drive; it is meant for people who love sbpcd
-so much that they do not want to miss it, even if they miss the drives. ;-)  
-
-If you configure "#define CDROM_PORT 0" in sbpcd.h, the auto-probing is
-initially disabled and needs an explicit kernel command to get activated.
-Once activated, it does not stop before success or end-of-list. This may be
-useful within "universal" CDROM installation boot floppies (but using the 
-loadable module would be better because it allows an "extended" auto-probing
-without fearing NE2000 cards).
-
-To shorten the auto-probing list to a single entry, set DISTRIBUTION 0 within
-sbpcd.h.
-
-
-Setting up address and interface type:
---------------------------------------
-
-If your I/O port address is not 0x340, you have to look for the #defines near
-the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and
-change CDROM_PORT to the address of your CDROM I/O port.
-
-Almost all of the "SoundBlaster compatible" cards behave like the no-sound
-interfaces, i.e. need SBPRO 0! 
-
-With "original" SB Pro cards, an initial setting of CD_volume through the
-sound cards MIXER register gets done.
-If you are using a "compatible" sound card of types "LaserMate" or "SPEA",
-you can set SOUND_BASE (in sbpcd.h) to get it done with your card, too...
-
-
-Using audio CDs:
-----------------
-
-Workman, WorkBone, xcdplayer, cdplayer and the nice little tool "cdplay" (see
-README.aztcd from the Aztech driver package) should work.
-
-The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer wants
-"/dev/rsr0", workman loves "/dev/sr0" or "/dev/cdrom" - so, do the appropriate
-links for using them without the need of supplying parameters.
-
-
-Copying audio tracks:
----------------------
-
-The following program will copy track 1 (or a piece of it) from an audio CD
-into the file "track01":
-
-/*=================== begin program ========================================*/
-/*
- * read an audio track from a CD
- *
- * (c) 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
- *          may be used & enhanced freely
- *
- * Due to non-existent sync bytes at the beginning of each audio frame (or due
- * to a firmware bug within all known drives?), it is currently a kind of
- * fortune if two consecutive frames fit together.
- * Usually, they overlap, or a little piece is missing. This happens in units
- * of 24-byte chunks. It has to get fixed by higher-level software (reading
- * until an overlap occurs, and then eliminate the overlapping chunks). 
- * ftp.gwdg.de:/pub/linux/misc/cdda2wav-sbpcd.*.tar.gz holds an example of
- * such an algorithm.
- * This example program further is missing to obtain the SubChannel data
- * which belong to each frame.
- *
- * This is only an example of the low-level access routine. The read data are
- * pure 16-bit CDDA values; they have to get converted to make sound out of
- * them.
- * It is no fun to listen to it without prior overlap/underlap correction!
- */
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <linux/cdrom.h>
-
-static struct cdrom_tochdr hdr;
-static struct cdrom_tocentry entry[101];
-static struct cdrom_read_audio arg;
-static u_char buffer[CD_FRAMESIZE_RAW];
-static int datafile, drive;
-static int i, j, limit, track, err;
-static char filename[32];
-
-main(int argc, char *argv[])
-{
-/*
- * open /dev/cdrom
- */
-  drive=open("/dev/cdrom", 0);
-  if (drive<0)
-    {
-      fprintf(stderr, "can't open drive.\n");
-      exit (-1);
-    }
-/*
- * get TocHeader
- */
-  fprintf(stdout, "getting TocHeader...\n");
-  err=ioctl(drive, CDROMREADTOCHDR, &hdr);
-  if (err!=0)
-    {
-      fprintf(stderr, "can't get TocHeader (error %d).\n", err);
-      exit (-1);
-    }
-  else
-    fprintf(stdout, "TocHeader: %d %d\n", hdr.cdth_trk0, hdr.cdth_trk1);
-/*
- * get and display all TocEntries
- */
-  fprintf(stdout, "getting TocEntries...\n");
-  for (i=1;i<=hdr.cdth_trk1+1;i++)
-    {
-      if (i!=hdr.cdth_trk1+1) entry[i].cdte_track = i;
-      else entry[i].cdte_track = CDROM_LEADOUT;
-      entry[i].cdte_format = CDROM_LBA;
-      err=ioctl(drive, CDROMREADTOCENTRY, &entry[i]);
-      if (err!=0)
-	{
-	  fprintf(stderr, "can't get TocEntry #%d (error %d).\n", i, err);
-	  exit (-1);
-	}
-      else
-	{
-	  fprintf(stdout, "TocEntry #%d: %1X %1X %06X %02X\n",
-		 entry[i].cdte_track,
-		 entry[i].cdte_adr,
-		 entry[i].cdte_ctrl,
-		 entry[i].cdte_addr.lba,
-		 entry[i].cdte_datamode);
-	}
-    }
-  fprintf(stdout, "got all TocEntries.\n");
-/*
- * ask for track number (not implemented here)
- */
-track=1;
-#if 0 /* just read a little piece (4 seconds) */
-entry[track+1].cdte_addr.lba=entry[track].cdte_addr.lba+300;
-#endif
-/*
- * read track into file
- */
-  sprintf(filename, "track%02d\0", track);
-  datafile=creat(filename, 0755);
-  if (datafile<0)
-    {
-      fprintf(stderr, "can't open datafile %s.\n", filename);
-      exit (-1);
-    }
-  arg.addr.lba=entry[track].cdte_addr.lba;
-  arg.addr_format=CDROM_LBA; /* CDROM_MSF would be possible here, too. */
-  arg.nframes=1;
-  arg.buf=&buffer[0];
-  limit=entry[track+1].cdte_addr.lba;
-  for (;arg.addr.lba<limit;arg.addr.lba++)
-    {
-      err=ioctl(drive, CDROMREADAUDIO, &arg);
-      if (err!=0)
-	{
-	  fprintf(stderr, "can't read abs. frame #%d (error %d).\n", 
-		 arg.addr.lba, err);
-	}
-      j=write(datafile, &buffer[0], CD_FRAMESIZE_RAW);
-      if (j!=CD_FRAMESIZE_RAW)
-	{
-	  fprintf(stderr,"I/O error (datafile) at rel. frame %d\n",
-			 arg.addr.lba-entry[track].cdte_addr.lba);
-	}
-      arg.addr.lba++;
-    }
-}
-/*===================== end program ========================================*/
-
-At ftp.gwdg.de:/pub/linux/misc/cdda2wav-sbpcd.*.tar.gz is an adapted version of
-Heiko Eissfeldt's digital-audio to .WAV converter (the original is there, too).
-This is preliminary, as Heiko himself will care about it.
-
-
-Known problems:
----------------
-
-Currently, the detection of disk change or removal is actively disabled.
-
-Most attempts to read the UPC/EAN code result in a stream of zeroes. All my
-drives are mostly telling there is no UPC/EAN code on disk or there is, but it
-is an all-zero number. I guess now almost no CD holds such a number.
-
-Bug reports, comments, wishes, donations (technical information is a donation,
-too :-) etc. to
-                         emoenke@gwdg.de
- or to my FIDO address:  Eberhard Moenkeberg, 2:2437/210.27
-
-SnailMail address, preferable for CD editors if they want to submit a free
-"cooperation" copy:
-                         Eberhard Moenkeberg
-                         Reinholdstr. 14
-                         D-37083 Goettingen
-                         Germany
----
-
-
-Appendix -- the "cdtester" utility:
-
-/*
- * cdtester.c -- test the audio functions of a CD driver
- *
- * (c) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>
- *          published under the GPL
- *
- *          made under heavy use of the "Tiny Audio CD Player"
- *          from Werner Zimmermann <zimmerma@rz.fht-esslingen.de>
- *          (see linux/drivers/block/README.aztcd)
- */
-#undef AZT_PRIVATE_IOCTLS /* not supported by every CDROM driver */
-#define SBP_PRIVATE_IOCTLS /* not supported by every CDROM driver */
-
-#include <stdio.h>
-#include <stdio.h>
-#include <malloc.h>
-#include <sys/ioctl.h>
-#include <linux/cdrom.h>
-
-#ifdef AZT_PRIVATE_IOCTLS
-#include <linux/aztcd.h>
-#endif AZT_PRIVATE_IOCTLS
-#ifdef SBP_PRIVATE_IOCTLS
-#include <linux/sbpcd.h>
-#include <linux/fs.h>
-#endif SBP_PRIVATE_IOCTLS
-
-struct cdrom_tochdr hdr;
-struct cdrom_tochdr tocHdr;
-struct cdrom_tocentry TocEntry[101];
-struct cdrom_tocentry entry;
-struct cdrom_multisession ms_info;
-struct cdrom_read_audio read_audio;
-struct cdrom_ti ti;
-struct cdrom_subchnl subchnl;
-struct cdrom_msf msf;
-struct cdrom_volctrl volctrl;
-#ifdef AZT_PRIVATE_IOCTLS
-union
-{
-	struct cdrom_msf msf;
-	unsigned char buf[CD_FRAMESIZE_RAW];
-} azt;
-#endif AZT_PRIVATE_IOCTLS
-int i, i1, i2, i3, j, k;
-unsigned char sequence=0;
-unsigned char command[80];
-unsigned char first=1, last=1;
-char *default_device="/dev/cdrom";
-char dev[20];
-char filename[20];
-int drive;
-int datafile;
-int rc;
-
-void help(void)
-{
-	printf("Available Commands:\n");
-	printf("STOP          s      EJECT        e       QUIT         q\n");
-	printf("PLAY TRACK    t      PAUSE        p       RESUME       r\n");
-	printf("NEXT TRACK    n      REPEAT LAST  l       HELP         h\n");
-	printf("SUBCHANNEL_Q  c      TRACK INFO   i       PLAY AT      a\n");
-	printf("READ          d      READ RAW     w       READ AUDIO   A\n");
-	printf("MS-INFO       M      TOC          T       START        S\n");
-	printf("SET EJECTSW   X      DEVICE       D       DEBUG        Y\n");
-	printf("AUDIO_BUFSIZ  Z      RESET        R       BLKRASET     B\n");
-	printf("SET VOLUME    v      GET VOLUME   V\n");
-}
-
-/*
- *  convert MSF number (3 bytes only) to Logical_Block_Address 
- */
-int msf2lba(u_char *msf)
-{
-	int i;
-	
-	i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
-	if (i<0) return (0);
-	return (i);
-}
-/*
- *  convert logical_block_address to m-s-f_number (3 bytes only)
- */
-void lba2msf(int lba, unsigned char *msf)
-{
-	lba += CD_BLOCK_OFFSET;
-	msf[0] = lba / (CD_SECS*CD_FRAMES);
-	lba %= CD_SECS*CD_FRAMES;
-	msf[1] = lba / CD_FRAMES;
-	msf[2] = lba % CD_FRAMES;
-}
-
-int init_drive(char *dev)
-{
-	unsigned char msf_ent[3];
-
-	/*
-	 * open the device
-	 */
-	drive=open(dev,0);
-	if (drive<0) return (-1);
-	/*
-	 * get TocHeader
-	 */
-	printf("getting TocHeader...\n");
-	rc=ioctl(drive,CDROMREADTOCHDR,&hdr);
-	if (rc!=0)
-	{
-		printf("can't get TocHeader (error %d).\n",rc);
-		return (-2);
-	}
-	else
-		first=hdr.cdth_trk0;
-		last=hdr.cdth_trk1;
-		printf("TocHeader: %d %d\n",hdr.cdth_trk0,hdr.cdth_trk1);
-	/*
-	 * get and display all TocEntries
-	 */
-	printf("getting TocEntries...\n");
-	for (i=1;i<=hdr.cdth_trk1+1;i++)
-	{
-		if (i!=hdr.cdth_trk1+1) TocEntry[i].cdte_track = i;
-		else TocEntry[i].cdte_track = CDROM_LEADOUT;
-		TocEntry[i].cdte_format = CDROM_LBA;
-		rc=ioctl(drive,CDROMREADTOCENTRY,&TocEntry[i]);
-		if (rc!=0)
-		{
-			printf("can't get TocEntry #%d (error %d).\n",i,rc);
-		}
-		else
-		{
-			lba2msf(TocEntry[i].cdte_addr.lba,&msf_ent[0]);
-			if (TocEntry[i].cdte_track==CDROM_LEADOUT)
-			{
-				printf("TocEntry #%02X: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
-				       TocEntry[i].cdte_track,
-				       TocEntry[i].cdte_adr,
-				       TocEntry[i].cdte_ctrl,
-				       msf_ent[0],
-				       msf_ent[1],
-				       msf_ent[2],
-				       TocEntry[i].cdte_addr.lba,
-				       TocEntry[i].cdte_datamode);
-			}
-			else
-			{
-				printf("TocEntry #%02d: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
-				       TocEntry[i].cdte_track,
-				       TocEntry[i].cdte_adr,
-				       TocEntry[i].cdte_ctrl,
-				       msf_ent[0],
-				       msf_ent[1],
-				       msf_ent[2],
-				       TocEntry[i].cdte_addr.lba,
-				       TocEntry[i].cdte_datamode);
-			}
-		}
-	}
-	return (hdr.cdth_trk1); /* number of tracks */
-}
-
-void display(int size,unsigned char *buffer)
-{
-	k=0;
-	getchar();
-	for (i=0;i<(size+1)/16;i++)
-	{
-		printf("%4d:",i*16);
-		for (j=0;j<16;j++)
-		{
-			printf(" %02X",buffer[i*16+j]);
-		}
-		printf("  ");
-		for (j=0;j<16;j++)
-		{
-			if (isalnum(buffer[i*16+j])) 
-				printf("%c",buffer[i*16+j]);
-			else
-				printf(".");
-		}
-		printf("\n"); 
-		k++;
-		if (k>=20)
-		{
-			printf("press ENTER to continue\n");
-			getchar();
-			k=0;
-		}
-	} 
-} 
-
-main(int argc, char *argv[])
-{
-	printf("\nTesting tool for a CDROM driver's audio functions V0.1\n");
-	printf("(C) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>\n");
-	printf("initializing...\n");
-	
-	rc=init_drive(default_device);
-	if (rc<0) printf("could not open %s (rc=%d).\n",default_device,rc);
-	help();
-	while (1)
-	{
-		printf("Give a one-letter command (h = help): ");
-		scanf("%s",command);
-		command[1]=0;
-		switch (command[0])
-		{
-		case 'D':
-			printf("device name (f.e. /dev/sbpcd3): ? ");
-			scanf("%s",&dev);
-			close(drive);
-			rc=init_drive(dev);
-			if (rc<0) printf("could not open %s (rc %d).\n",dev,rc);
-			break;
-		case 'e':
-			rc=ioctl(drive,CDROMEJECT);
-			if (rc<0) printf("CDROMEJECT: rc=%d.\n",rc);
-			break;
-		case 'p':
-			rc=ioctl(drive,CDROMPAUSE);
-			if (rc<0) printf("CDROMPAUSE: rc=%d.\n",rc);
-			break;
-		case 'r':
-			rc=ioctl(drive,CDROMRESUME);
-			if (rc<0) printf("CDROMRESUME: rc=%d.\n",rc);
-			break;
-		case 's':
-			rc=ioctl(drive,CDROMSTOP);
-			if (rc<0) printf("CDROMSTOP: rc=%d.\n",rc);
-			break;
-		case 'S':
-			rc=ioctl(drive,CDROMSTART);
-			if (rc<0) printf("CDROMSTART: rc=%d.\n",rc);
-			break;
-		case 't':
-			rc=ioctl(drive,CDROMREADTOCHDR,&tocHdr);
-			if (rc<0)
-			{
-				printf("CDROMREADTOCHDR: rc=%d.\n",rc);
-				break;
-			}
-			first=tocHdr.cdth_trk0;
-			last= tocHdr.cdth_trk1;
-			if ((first==0)||(first>last))
-			{
-				printf ("--got invalid TOC data.\n");
-			}
-			else
-			{
-				printf("--enter track number(first=%d, last=%d): ",first,last);
-				scanf("%d",&i1);
-				ti.cdti_trk0=i1;
-				if (ti.cdti_trk0<first) ti.cdti_trk0=first;
-				if (ti.cdti_trk0>last) ti.cdti_trk0=last;
-				ti.cdti_ind0=0;
-				ti.cdti_trk1=last;
-				ti.cdti_ind1=0;
-				rc=ioctl(drive,CDROMSTOP);
-				rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
-				if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
-			}
-			break;
-		case 'n':
-			rc=ioctl(drive,CDROMSTOP);
-			if (++ti.cdti_trk0>last) ti.cdti_trk0=last;
-			ti.cdti_ind0=0;
-			ti.cdti_trk1=last;
-			ti.cdti_ind1=0;
-			rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
-			if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
-			break;
-		case 'l':
-			rc=ioctl(drive,CDROMSTOP);
-			if (--ti.cdti_trk0<first) ti.cdti_trk0=first;
-			ti.cdti_ind0=0;
-			ti.cdti_trk1=last;
-			ti.cdti_ind1=0;
-			rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
-			if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
-			break;
-		case 'c':
-			subchnl.cdsc_format=CDROM_MSF;
-			rc=ioctl(drive,CDROMSUBCHNL,&subchnl);
-			if (rc<0) printf("CDROMSUBCHNL: rc=%d.\n",rc);
-			else
-			{
-				printf("AudioStatus:%s  Track:%d  Mode:%d  MSF=%02d:%02d:%02d\n",
-				       subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",
-				       subchnl.cdsc_trk,subchnl.cdsc_adr, 
-				       subchnl.cdsc_absaddr.msf.minute,
-				       subchnl.cdsc_absaddr.msf.second,
-				       subchnl.cdsc_absaddr.msf.frame);
-			}
-			break;              
-		case 'i':
-			printf("Track No.: ");
-			scanf("%d",&i1);
-			entry.cdte_track=i1;
-			if (entry.cdte_track<first) entry.cdte_track=first;
-			if (entry.cdte_track>last)  entry.cdte_track=last;
-			entry.cdte_format=CDROM_MSF;
-			rc=ioctl(drive,CDROMREADTOCENTRY,&entry);
-			if (rc<0) printf("CDROMREADTOCENTRY: rc=%d.\n",rc);
-			else
-			{
-				printf("Mode %d Track, starts at %02d:%02d:%02d\n",
-				       entry.cdte_adr,
-				       entry.cdte_addr.msf.minute,
-				       entry.cdte_addr.msf.second,
-				       entry.cdte_addr.msf.frame);
-			}
-			break;
-		case 'a':
-			printf("Address (min:sec:frm)  ");
-			scanf("%d:%d:%d",&i1,&i2,&i3);
-			msf.cdmsf_min0=i1;
-			msf.cdmsf_sec0=i2;
-			msf.cdmsf_frame0=i3;
-			if (msf.cdmsf_sec0>59) msf.cdmsf_sec0=59;
-			if (msf.cdmsf_frame0>74) msf.cdmsf_frame0=74;
-			lba2msf(TocEntry[last+1].cdte_addr.lba-1,&msf.cdmsf_min1);
-			rc=ioctl(drive,CDROMSTOP);
-			rc=ioctl(drive,CDROMPLAYMSF,&msf);
-			if (rc<0) printf("CDROMPLAYMSF: rc=%d.\n",rc);
-			break;
-		case 'V':
-			rc=ioctl(drive,CDROMVOLREAD,&volctrl);
-			if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
-			printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
-			break;  
-		case 'R':
-			rc=ioctl(drive,CDROMRESET);
-			if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
-			break;
-		case 'B': /* set the driver's (?) read ahead value */
-			printf("enter read-ahead size: ? ");
-			scanf("%d",&i);
-			rc=ioctl(drive,BLKRASET,i);
-			if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
-			break;
-#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
-		case 'd':
-			printf("Address (min:sec:frm)  ");
-			scanf("%d:%d:%d",&i1,&i2,&i3);
-			azt.msf.cdmsf_min0=i1;
-			azt.msf.cdmsf_sec0=i2;
-			azt.msf.cdmsf_frame0=i3;
-			if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
-			if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
-			rc=ioctl(drive,CDROMREADMODE1,&azt.msf);
-			if (rc<0) printf("CDROMREADMODE1: rc=%d.\n",rc);
-			else display(CD_FRAMESIZE,azt.buf);
-			break;
-		case 'w':
-			printf("Address (min:sec:frame)  ");
-			scanf("%d:%d:%d",&i1,&i2,&i3);
-			azt.msf.cdmsf_min0=i1;
-			azt.msf.cdmsf_sec0=i2;
-			azt.msf.cdmsf_frame0=i3;
-			if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
-			if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
-			rc=ioctl(drive,CDROMREADMODE2,&azt.msf);
-			if (rc<0) printf("CDROMREADMODE2: rc=%d.\n",rc);
-			else display(CD_FRAMESIZE_RAW,azt.buf); /* currently only 2336 */
-			break;  
-#endif
-		case 'v':
-			printf("--Channel 0 (Left)  (0-255): ");
-			scanf("%d",&i1);
-			volctrl.channel0=i1;
-			printf("--Channel 1 (Right) (0-255): ");
-			scanf("%d",&i1);
-			volctrl.channel1=i1;
-			volctrl.channel2=0;
-			volctrl.channel3=0;
-			rc=ioctl(drive,CDROMVOLCTRL,&volctrl);
-			if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
-			break;  
-		case 'q':
-			close(drive);
-			exit(0);
-		case 'h':
-			help();
-			break;
-		case 'T': /* display TOC entry - without involving the driver */
-			scanf("%d",&i);
-			if ((i<hdr.cdth_trk0)||(i>hdr.cdth_trk1))
-				printf("invalid track number.\n");
-			else
-				printf("TocEntry %02d: adr=%01X ctrl=%01X msf=%02d:%02d:%02d mode=%02X\n",
-				       TocEntry[i].cdte_track,
-				       TocEntry[i].cdte_adr,
-				       TocEntry[i].cdte_ctrl,
-				       TocEntry[i].cdte_addr.msf.minute,
-				       TocEntry[i].cdte_addr.msf.second,
-				       TocEntry[i].cdte_addr.msf.frame,
-				       TocEntry[i].cdte_datamode);
-			break;
-		case 'A': /* read audio data into file */
-			printf("Address (min:sec:frm) ? ");
-			scanf("%d:%d:%d",&i1,&i2,&i3);
-			read_audio.addr.msf.minute=i1;
-			read_audio.addr.msf.second=i2;
-			read_audio.addr.msf.frame=i3;
-			read_audio.addr_format=CDROM_MSF;
-			printf("# of frames ? ");
-			scanf("%d",&i1);
-			read_audio.nframes=i1;
-			k=read_audio.nframes*CD_FRAMESIZE_RAW;
-			read_audio.buf=malloc(k);
-			if (read_audio.buf==NULL)
-			{
-				printf("can't malloc %d bytes.\n",k);
-				break;
-			}
-			sprintf(filename,"audio_%02d%02d%02d_%02d.%02d\0",
-				read_audio.addr.msf.minute,
-				read_audio.addr.msf.second,
-				read_audio.addr.msf.frame,
-				read_audio.nframes,
-				++sequence);
-			datafile=creat(filename, 0755);
-			if (datafile<0)
-			{
-				printf("can't open datafile %s.\n",filename);
-				break;
-			}
-			rc=ioctl(drive,CDROMREADAUDIO,&read_audio);
-			if (rc!=0)
-			{
-				printf("CDROMREADAUDIO: rc=%d.\n",rc);
-			}
-			else
-			{
-				rc=write(datafile,&read_audio.buf,k);
-				if (rc!=k) printf("datafile I/O error (%d).\n",rc);
-			}
-			close(datafile);
-			break;
-		case 'X': /* set EJECT_SW (0: disable, 1: enable auto-ejecting) */
-			scanf("%d",&i);
-			rc=ioctl(drive,CDROMEJECT_SW,i);
-			if (rc!=0)
-				printf("CDROMEJECT_SW: rc=%d.\n",rc);
-			else
-				printf("EJECT_SW set to %d\n",i);
-			break;
-		case 'M': /* get the multisession redirection info */
-			ms_info.addr_format=CDROM_LBA;
-			rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
-			if (rc!=0)
-			{
-				printf("CDROMMULTISESSION(lba): rc=%d.\n",rc);
-			}
-			else
-			{
-				if (ms_info.xa_flag) printf("MultiSession offset (lba): %d (0x%06X)\n",ms_info.addr.lba,ms_info.addr.lba);
-				else
-				{
-					printf("this CD is not an XA disk.\n");
-					break;
-				}
-			}
-			ms_info.addr_format=CDROM_MSF;
-			rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
-			if (rc!=0)
-			{
-				printf("CDROMMULTISESSION(msf): rc=%d.\n",rc);
-			}
-			else
-			{
-				if (ms_info.xa_flag)
-					printf("MultiSession offset (msf): %02d:%02d:%02d (0x%02X%02X%02X)\n",
-					       ms_info.addr.msf.minute,
-					       ms_info.addr.msf.second,
-					       ms_info.addr.msf.frame,
-					       ms_info.addr.msf.minute,
-					       ms_info.addr.msf.second,
-					       ms_info.addr.msf.frame);
-				else printf("this CD is not an XA disk.\n");
-			}
-			break;
-#ifdef SBP_PRIVATE_IOCTLS
-		case 'Y': /* set the driver's message level */
-#if 0 /* not implemented yet */
-			printf("enter switch name (f.e. DBG_CMD): ");
-			scanf("%s",&dbg_switch);
-			j=get_dbg_num(dbg_switch);
-#else
-			printf("enter DDIOCSDBG switch number: ");
-			scanf("%d",&j);
-#endif
-			printf("enter 0 for \"off\", 1 for \"on\": ");
-			scanf("%d",&i);
-			if (i==0) j|=0x80;
-			printf("calling \"ioctl(drive,DDIOCSDBG,%d)\"\n",j);
-			rc=ioctl(drive,DDIOCSDBG,j);
-			printf("DDIOCSDBG: rc=%d.\n",rc);
-			break;
-		case 'Z': /* set the audio buffer size */
-			printf("# frames wanted: ? ");
-			scanf("%d",&j);
-			rc=ioctl(drive,CDROMAUDIOBUFSIZ,j);
-			printf("%d frames granted.\n",rc);
-			break;
-#endif SBP_PRIVATE_IOCTLS
-		default:
-			printf("unknown command: \"%s\".\n",command);
-			break;
-		}
-	}
-}
-/*==========================================================================*/
-
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.sjcd linux/drivers/block/README.sjcd
--- v1.3.35/linux/drivers/block/README.sjcd	Mon Oct 16 18:38:21 1995
+++ linux/drivers/block/README.sjcd	Thu Jan  1 02:00:00 1970
@@ -1,80 +0,0 @@
- -- README.sjcd
-				80% of the work takes 20% of the time,
-				20% of the work takes 80% of the time...
-						(Murphy law)
-
-				Once started, training can not be stopped...
-						(StarWars)
-
-This is the README for the sjcd cdrom driver, version 1.5.
-
-This file is meant as a tips & tricks edge for the usage of the SANYO CDR-H94A
-cdrom drive. It will grow as the questions arise. ;-)
-Since the drive often comes with an ISP16 soundcard, which can be used
-as cdrom interface, this is also the place for ISP16 related issues.
-
-The driver should work with any SoundBlaster/Panasonic style CDROM interface,
-including the "soft configurable" MediaMagic sound card.
-To make this sound card (and others like "Mozart") working, it has to get 
-"configured" by software.
-The suggestion to configure the ISP16 soundcard by booting DOS and 
-a warm reboot to boot Linux somehow doesn't work, at least not
-on Eric's machine (IPC P90), with the version of the ISP16
-card he has (there appear to be at least two versions: Eric's card with
-no jumpered IDE support and OPTi 82C928 chip, and Vadim's version
-with a jumper to enable IDE support, probably with a OPTi 82C929 chip).
-Therefore detection and configuration of the ISP16 interfaces is included
-in the driver.
-If we should support any other interfaces (which cannot be configured
-through DOS) or if there are any more ISP16 types, please let us
-know (maarel@marin.nl) and we'll see.
-
-Otherwise, you should boot DOS once (and by this, run the "configuration driver")
-and then switch to Linux by use of CTRL-ALT-DEL. Each use of the RESET
-button or the power switch makes this procedure necessary again.
-If no ISP16 is detected, there's no harm done; a card configured trough DOS
-may still work as expected.
-
-As of version 1.4 sound through the speakers is supported; only for MSS-mode 
-and no volume controle yet.
-
-PAUSE and STOP ioctl commands don't seem to work yet.
-
-ISP16 configuration routines reside at Vadim's server
-      ftp.rbrf.ru:/linux/mediamagic/
-and at Eberhard's mirror
-      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
-
-Leo Spiekman's configuration routines for the ISP-16 card can get found at
-      dutette.et.tudelft.nl:/pub/linux/
-and at Eberhard's mirror
-      ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
-
-Eric van der Maarel's routines are included in sjcd.c.
-This, and any related stuff may be found by anonymous ftp at
-      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
-
-The device major for sjcd is 18, and minor is 0. Create a block special
-file in your /dev directory (e.g., /dev/sjcd) with these numbers.
-(For those who don't know, being root and doing the following should do the trick:
-  mknod -m 644 /dev/sjcd b 18 0
-and mount the cdrom by /dev/sjcd).
-
-The default configuration parameters are:
-  base address 0x340
-  no irq
-  no dma
-As of version 1.2, setting base address, irq and dma at boot time is supported
-through the use of command line options: type at the "boot:" prompt:
-  linux sjcd=<base address>,<irq>,<dma>
-(where your kernel is assumed to be called by saying "linux" to
-the boot manager).
-
-If something is wrong, e-mail to		vadim@rbrf.ru
-					or 	vadim@ipsun.ras.ru
-					or	model@cecmow.enet.dec.com
-
-It happens sometimes that Vadim is not reachable by mail. For these
-instances, Eric van der Maarel (maarel@marin.nl) will help, too.
-
-		Vadim V. Model, Eric van der Maarel, Eberhard Moenkeberg
diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.sonycd535 linux/drivers/block/README.sonycd535
--- v1.3.35/linux/drivers/block/README.sonycd535	Fri Jul  7 08:54:44 1995
+++ linux/drivers/block/README.sonycd535	Thu Jan  1 02:00:00 1970
@@ -1,121 +0,0 @@
-              README FOR LINUX SONY CDU-535/531 DRIVER
-              ========================================
-
-This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux.
-I do not think I have the documentation to add features like DMA support
-so if anyone else wants to pursue it or help me with it, please do.
-(I need to see what was done for the CDU-31A driver -- perhaps I can
-steal some of that code.)
-
-This is a Linux device driver for the Sony CDU-535 CDROM drive.  This is
-one of the older Sony drives with its own interface card (Sony bus).
-The DOS driver for this drive is named SONY_CDU.SYS - when you boot DOS
-your drive should be identified as a SONY CDU-535.  The driver works
-with a CDU-531 also.  One user reported that the driver worked on drives
-OEM'ed by Procomm, drive and interface board were labelled Procomm.
-
-The Linux driver is based on Corey Minyard's sonycd 0.3 driver for
-the CDU-31A.  Ron Jeppesen just changed the commands that were sent
-to the drive to correspond to the CDU-535 commands and registers.
-There were enough changes to let bugs creep in but it seems to be stable.
-Ron was able to tar an entire CDROM (should read all blocks) and built
-ghostview and xfig off Walnut Creek's X11R5/GNU CDROM.  xcdplayer and
-workman work with the driver.  Others have used the driver without
-problems except those dealing with wait loops (fixed in third release).
-Like Minyard's original driver this one uses a polled interface (this
-is also the default setup for the DOS driver).  It has not been tried
-with interrupts or DMA enabled on the board.
-
-REQUIREMENTS
-============
-
-	- Sony CDU-535 drive, preferably without interrupts and DMA 
-	  enabled on the card.
-
-	- Drive must be set up as unit 1.  Only the first unit will be 
-	  recognized
-
-	- you must enter your interface address into 
-          /usr/src/linux/include/linux/sonycd535.h and build the
-          appropriate kernel or use the "kernel command line" parameter
-                sonycd535=0x320
-          with the correct interface address.
-
-NOTES:
-======
-
-1) The drive MUST be turned on when booting or it will not be recognized!
-   (but see comments on modularized version below)
-
-2) when the cdrom device is opened the eject button is disabled to keep the
-   user from ejecting a mounted disk and replacing it with another.
-   Unfortunately xcdplayer and workman also open the cdrom device so you
-   have to use the eject button in the software.  Keep this in mind if your
-   cdrom player refuses to give up its disk -- exit workman or xcdplayer, or
-   umount the drive if it has been mounted.
-
-THANKS
-======
-
-Many thanks to Ron Jeppesen (ronj.an@site007.saic.com) for getting
-this project off the ground.  He wrote the initial release
-and the first two patches to this driver (0.1, 0.2, and 0.3).
-Thanks also to Eberhard Moenkeberg (emoenke@gwdg.de) for prodding
-me to place this code into the mainstream Linux source tree
-(as of Linux version 1.1.91), as well as some patches to make
-it a better device citizen.  Further thanks to "S. Joel Katz"
-<stimpson@panix.com> for his MODULE patches (see details below),
-Porfiri Claudio <C.Porfiri@nisms.tei.ericsson.se> for patches
-to make the driver work with the older CDU-510/515 series, and
-Heiko Eissfeldt <heiko@colossus.escape.de> for pointing out that
-the verify_area() checks were ignoring the results of said checks.
-
-(Acknowledgments from Ron Jeppesen in the 0.3 release:)
-Thanks to Corey Minyard who wrote the original CDU-31A driver on which
-this driver is based.  Thanks to Ken Pizzini and Bob Blair who provided 
-patches and feedback on the first release of this driver.
-
-Ken Pizzini
-ken@halcyon.com
-
-------------------------------------------------------------------------------
-(The following is from Joel Katz <Stimpson@Panix.COM>.)
-
-	To build a version of sony535.o that can be installed as a module,
-use the following command:
-
-gcc -c -D__KERNEL__ -DMODULE -O2 sonycd535.c -o sonycd535.o
-
-	To install the module, simply type:
-
-insmod sony535.o
-	or
-insmod sony535.o sonycd535=<address>
-
-	And to remove it:
-
-rmmod sony535
-
-	The code checks to see if MODULE is defined and behaves as it used
-to if MODULE is not defined. That means your patched file should behave
-exactly as it used to if compiled into the kernel.
-
-	I have an external drive, and I usually leave it powered off. I used
-to have to reboot if I needed to use the CDROM drive. Now I don't.
-
-	Even if you have an internal drive, why waste the 268K of memory
-(unswappable) that the driver uses if you use your CD-ROM drive infrequently?
-
-	This driver will not install (whether compiled in or loaded as a
-module) if the CDROM drive is not available during its initialization. This
-means that you can have the driver compiled into the kernel and still load
-the module later (assuming the driver doesn't install itself during
-power-on). This only wastes 12K when you boot with the CDROM drive off.
-
-	This is what I usually do; I leave the driver compiled into the
-kernel, but load it as a module if I powered the system up with the drive
-off and then later decided to use the CDROM drive.
-
-	Since the driver only uses a single page to point to the chunks,
-attempting to set the buffer cache to more than 2 Megabytes would be very
-bad; don't do that.
diff -u --recursive --new-file v1.3.35/linux/drivers/block/aztcd.c linux/drivers/block/aztcd.c
--- v1.3.35/linux/drivers/block/aztcd.c	Mon Oct 16 18:38:22 1995
+++ linux/drivers/block/aztcd.c	Thu Jan  1 02:00:00 1970
@@ -1,2088 +0,0 @@
-#define AZT_VERSION "1.80"
-/*      $Id: aztcd.c,v 1.80 1995/10/11 19:35:03 root Exp root $
-	linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
-
-	Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
-
-	based on Mitsumi CDROM driver by  Martin Hariss and preworks by
-	Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby 
-	Schirmer.
-
-	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; if not, write to the Free Software
-	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-	HISTORY
-	V0.0    Adaption to Adaptec CD268-01A Version 1.3
-		Version is PRE_ALPHA, unresolved points:
-		1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW
-		   thus driver causes CPU overhead and is very slow 
-		2. could not find a way to stop the drive, when it is
-		   in data read mode, therefore I had to set
-		   msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one
-		   frame can be read in sequence, this is also the reason for
-		3. getting 'timeout in state 4' messages, but nevertheless
-		   it works
-		W.Zimmermann, Oct. 31, 1994
-	V0.1    Version is ALPHA, problems #2 and #3 resolved.  
-		W.Zimmermann, Nov. 3, 1994
-	V0.2    Modification to some comments, debugging aids for partial test
-		with Borland C under DOS eliminated. Timer interrupt wait 
-		STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented; 
-		use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_
-		SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy 
-		waiting seems better to me than interrupt rescheduling.
-		Besides that, when used in the wrong place, STEN_LOW_WAIT causes
-		kernel panic.
-		In function aztPlay command ACMD_PLAY_AUDIO added, should make
-		audio functions work. The Aztech drive needs different commands
-		to read data tracks and play audio tracks.
-		W.Zimmermann, Nov. 8, 1994
-	V0.3    Recognition of missing drive during boot up improved (speeded up).
-		W.Zimmermann, Nov. 13, 1994
-	V0.35   Rewrote the control mechanism in azt_poll (formerly mcd_poll) 
-		including removal of all 'goto' commands. :-); 
-		J. Nardone, Nov. 14, 1994
-	V0.4    Renamed variables and constants to 'azt' instead of 'mcd'; had
-		to make some "compatibility" defines in azt.h; please note,
-		that the source file was renamed to azt.c, the include file to
-		azt.h                
-		Speeded up drive recognition during init (will be a little bit 
-		slower than before if no drive is installed!); suggested by
-		Robby Schirmer.
-		read_count declared volatile and set to AZT_BUF_SIZ to make
-		drive faster (now 300kB/sec, was 60kB/sec before, measured
-		by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096';
-		different AZT_BUF_SIZes were test, above 16 no further im-
-		provement seems to be possible; suggested by E.Moenkeberg.
-		W.Zimmermann, Nov. 18, 1994
-	V0.42   Included getAztStatus command in GetQChannelInfo() to allow
-		reading Q-channel info on audio disks, if drive is stopped, 
-		and some other bug fixes in the audio stuff, suggested by 
-		Robby Schirmer.
-		Added more ioctls (reading data in mode 1 and mode 2).
-		Completely removed the old azt_poll() routine.
-		Detection of ORCHID CDS-3110 in aztcd_init implemented.
-		Additional debugging aids (see the readme file).
-		W.Zimmermann, Dec. 9, 1994  
-	V0.50   Autodetection of drives implemented.
-		W.Zimmermann, Dec. 12, 1994
-	V0.52   Prepared for including in the standard kernel, renamed most
-		variables to contain 'azt', included autoconf.h
-		W.Zimmermann, Dec. 16, 1994        
-	V0.6    Version for being included in the standard Linux kernel.
-		Renamed source and header file to aztcd.c and aztcd.h
-		W.Zimmermann, Dec. 24, 1994
-	V0.7    Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case
-		CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl,
-		which causes kernel crashes when playing audio, changed 
-		include-files (config.h instead of autoconf.h, removed
-		delay.h)
-		W.Zimmermann, Jan. 8, 1995
-	V0.72   Some more modifications for adaption to the standard kernel.
-		W.Zimmermann, Jan. 16, 1995
-        V0.80   aztcd is now part of the standard kernel since version 1.1.83.
-                Modified the SET_TIMER and CLEAR_TIMER macros to comply with
-                the new timer scheme.
-                W.Zimmermann, Jan. 21, 1995
-        V0.90   Included CDROMVOLCTRL, but with my Aztech drive I can only turn
-                the channels on and off. If it works better with your drive, 
-                please mail me. Also implemented ACMD_CLOSE for CDROMSTART.
-                W.Zimmermann, Jan. 24, 1995
-        V1.00   Implemented close and lock tray commands. Patches supplied by
-		Frank Racis        
-                Added support for loadable MODULEs, so aztcd can now also be
-                loaded by insmod and removed by rmmod during run time
-                Werner Zimmermann, Mar. 24, 95
-        V1.10   Implemented soundcard configuration for Orchid CDS-3110 drives
-                connected to Soundwave32 cards. Release for LST 2.1.
-                (still experimental)
-                Werner Zimmermann, May 8, 95
-        V1.20   Implemented limited support for DOSEMU0.60's cdrom.c. Now it works, but
-                sometimes DOSEMU may hang for 30 seconds or so. A fully functional ver-
-                sion needs an update of Dosemu0.60's cdrom.c, which will come with the 
-                next revision of Dosemu.
-                Also Soundwave32 support now works.
-                Werner Zimmermann, May 22, 95
-	V1.30   Auto-eject feature. Inspired by Franc Racis (racis@psu.edu)
-	        Werner Zimmermann, July 4, 95
-	V1.40   Started multisession support. Implementation copied from mcdx.c
-	        by Heiko Schlittermann. Not tested yet.
-	        Werner Zimmermann, July 15, 95
-        V1.50   Implementation of ioctl CDROMRESET, continued multisession, began
-                XA, but still untested. Heavy modifications to drive status de-
-                tection.
-                Werner Zimmermann, July 25, 95
-        V1.60   XA support now should work. Speeded up drive recognition in cases, 
-                where no drive is installed.
-                Werner Zimmermann, August 8, 1995
-        V1.70   Multisession support now is completed, but there is still not 
-                enough testing done. If you can test it, please contact me. For
-                details please read README.aztcd.
-                Werner Zimmermann, August 19, 1995
-        V1.80   Modification to suit the new kernel boot procedure introduced
-                with kernel 1.3.33. Will definitely not work with older kernels.
-                Programming done by Linus himself.
-                Werner Zimmermann, October 11, 1995
-	NOTE: 
-	Points marked with ??? are questionable !
-*/
-#include <linux/major.h>
-#include <linux/config.h>
-
-#ifdef MODULE
-# include <linux/module.h>
-# include <linux/version.h>
-# ifndef CONFIG_MODVERSIONS
-    char kernel_version[]= UTS_RELEASE;
-# endif
-#define aztcd_init init_module
-#endif
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#define MAJOR_NR AZTECH_CDROM_MAJOR 
-
-#include "blk.h"
-
-#ifdef MODULE
-#else
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
-#endif
-
-#include <linux/aztcd.h>
-
-#define SET_TIMER(func, jifs)   delay_timer.expires = jiffies + (jifs); \
-                                delay_timer.function = (void *) (func); \
-                                add_timer(&delay_timer); 
-
-#define CLEAR_TIMER             del_timer(&delay_timer);
-
-#define RETURNM(message,value) {printk("aztcd: Warning: %s failed\n",message);\
-                                return value;}
-#define RETURN(message)        {printk("aztcd: Warning: %s failed\n",message);\
-                                return;}
-
-static int aztPresent = 0;
-
-#if 0
-#define AZT_TEST
-#define AZT_TEST1 /* <int-..> */
-#define AZT_TEST2 /* do_aztcd_request */
-#define AZT_TEST3 /* AZT_S_state */
-#define AZT_TEST4 /* QUICK_LOOP-counter */
-#define AZT_TEST5 /* port(1) state */
-#define AZT_DEBUG
-#define AZT_DEBUG_MULTISESSION
-#endif
-
-#define CURRENT_VALID \
-  (CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
-   && CURRENT -> sector != -1)
-
-#define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
-#define AZT_BUF_SIZ 16
-
-static volatile int azt_transfer_is_active=0;
-
-static char azt_buf[CD_FRAMESIZE_RAW*AZT_BUF_SIZ];/*buffer for block size conversion*/
-#if AZT_PRIVATE_IOCTLS
-static char buf[CD_FRAMESIZE_RAW];              /*separate buffer for the ioctls*/
-#endif
-
-static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn;
-static volatile int azt_buf_in, azt_buf_out = -1;
-static volatile int azt_error=0;
-static int azt_open_count=0;
-enum azt_state_e 
-{ AZT_S_IDLE,    /* 0 */
-  AZT_S_START,   /* 1 */
-  AZT_S_MODE,    /* 2 */
-  AZT_S_READ,    /* 3 */
-  AZT_S_DATA,    /* 4 */
-  AZT_S_STOP,    /* 5 */
-  AZT_S_STOPPING /* 6 */
-};
-static volatile enum azt_state_e azt_state = AZT_S_IDLE;
-#ifdef AZT_TEST3
-static volatile enum azt_state_e azt_state_old = AZT_S_STOP;  
-static volatile int azt_st_old = 0;
-#endif
-enum azt_read_modes 
-{ AZT_MODE_0,     /*read mode for audio disks, not supported by Aztech firmware*/
-  AZT_MODE_1,     /*read mode for normal CD-ROMs*/
-  AZT_MODE_2      /*read mode for XA CD-ROMs*/
-};
-static volatile enum azt_read_modes azt_read_mode = AZT_MODE_1;
-
-static int azt_mode = -1;
-static volatile int azt_read_count = 1;
-
-#define READ_TIMEOUT 3000
-
-#define azt_port aztcd  /*needed for the modutils*/
-static short azt_port = AZT_BASE_ADDR;
-
-static char  azt_cont = 0;
-static char  azt_init_end = 0;
-static char  azt_auto_eject = AZT_AUTO_EJECT;
-
-static int AztTimeout, AztTries;
-static struct wait_queue *azt_waitq = NULL; 
-static struct timer_list delay_timer = { NULL, NULL, 0, 0, NULL };
-
-static struct azt_DiskInfo DiskInfo;
-static struct azt_Toc Toc[MAX_TRACKS];
-static struct azt_Play_msf azt_Play;
-
-static int  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-static char aztDiskChanged = 1;
-static char aztTocUpToDate = 0;
-
-static void azt_transfer(void);
-static void azt_poll(void);
-static void azt_invalidate_buffers(void);
-static void do_aztcd_request(void);
-static void azt_hsg2msf(long hsg, struct msf *msf);
-static void azt_bin2bcd(unsigned char *p);
-static int  azt_bcd2bin(unsigned char bcd);
-static int  aztStatus(void);
-static int  getAztStatus(void);
-static int  aztSendCmd(int cmd);
-static int  sendAztCmd(int cmd, struct azt_Play_msf *params);
-static int  aztGetQChannelInfo(struct azt_Toc *qp);
-static int  aztUpdateToc(void);
-static int  aztGetDiskInfo(void);
-#if AZT_MULTISESSION 
-  static int  aztGetMultiDiskInfo(void);
-#endif
-static int  aztGetToc(int multi);
-static int  aztGetValue(unsigned char *result);
-static void aztStatTimer(void);
-static void aztCloseDoor(void);
-static void aztLockDoor(void);
-static void aztUnlockDoor(void);
-
-static unsigned char aztIndatum;
-static unsigned long aztTimeOutCount;
-static int aztCmd = 0;
-
-/* Macros for the drive hardware interface handshake, these macros use
-   busy waiting */
-/* Wait for OP_OK = drive answers with AFL_OP_OK after receiving a command*/
-# define OP_OK op_ok()
-void op_ok(void)
-{ aztTimeOutCount=0; 
-  do { aztIndatum=inb(DATA_PORT);
-       aztTimeOutCount++;
-       if (aztTimeOutCount>=AZT_TIMEOUT)
-	{ printk("aztcd: Error Wait OP_OK\n");
-	  break;
-	}
-     } while (aztIndatum!=AFL_OP_OK);
-}
-
-/* Wait for PA_OK = drive answers with AFL_PA_OK after receiving parameters*/
-# define PA_OK pa_ok()
-void pa_ok(void)
-{ aztTimeOutCount=0; 
-  do { aztIndatum=inb(DATA_PORT);
-       aztTimeOutCount++;
-       if (aztTimeOutCount>=AZT_TIMEOUT)
-	{ printk("aztcd: Error Wait PA_OK\n");
-	  break;
-	}
-     } while (aztIndatum!=AFL_PA_OK);
-}
-     
-/* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/
-# define STEN_LOW  sten_low()
-void sten_low(void)
-{ aztTimeOutCount=0; 
-  do { aztIndatum=inb(STATUS_PORT);
-       aztTimeOutCount++;
-       if (aztTimeOutCount>=AZT_TIMEOUT)
-	{ if (azt_init_end) printk("aztcd: Error Wait STEN_LOW commands:%x\n",aztCmd);
-	  break;
-	}
-     } while (aztIndatum&AFL_STATUS);
-}
-
-/* Wait for DTEN=Low = handshake signal 'Data available'*/
-# define DTEN_LOW dten_low()
-void dten_low(void)
-{ aztTimeOutCount=0; 
-  do { aztIndatum=inb(STATUS_PORT);
-       aztTimeOutCount++;
-       if (aztTimeOutCount>=AZT_TIMEOUT)
-	{ printk("aztcd: Error Wait DTEN_OK\n");
-	  break;
-	}
-     } while (aztIndatum&AFL_DATA);
-}
-
-/* 
- * Macro for timer wait on STEN=Low, should only be used for 'slow' commands;
- * may cause kernel panic when used in the wrong place
-*/
-#define STEN_LOW_WAIT   statusAzt()
-void statusAzt(void)
-{ AztTimeout = AZT_STATUS_DELAY;
-  SET_TIMER(aztStatTimer, HZ/100); 
-  sleep_on(&azt_waitq);
-  if (AztTimeout <= 0) printk("aztcd: Error Wait STEN_LOW_WAIT command:%x\n",aztCmd);
-  return;
-}
-
-static void aztStatTimer(void)
-{ if (!(inb(STATUS_PORT) & AFL_STATUS))
-     { wake_up(&azt_waitq);
-       return;
-     }
-  AztTimeout--;
-  if (AztTimeout <= 0)
-     { wake_up(&azt_waitq);
-       printk("aztcd: Error aztStatTimer: Timeout\n");
-       return;
-     }
-  SET_TIMER(aztStatTimer, HZ/100);
-}
-
-void aztcd_setup(char *str, int *ints)
-{  if (ints[0] > 0)
-      azt_port = ints[1];
-   if (ints[0] > 1)
-      azt_cont = ints[2];
-}
-
-/*
- * Subroutines to automatically close the door (tray) and 
- * lock it closed when the cd is mounted.  Leave the tray
- * locking as an option
- */
-static void aztCloseDoor(void)
-{
-  aztSendCmd(ACMD_CLOSE);
-  STEN_LOW;
-  return;
-}
-
-static void aztLockDoor(void)
-{
-#if AZT_ALLOW_TRAY_LOCK
-  aztSendCmd(ACMD_LOCK);
-  STEN_LOW;
-#endif
-  return;
-}
-
-static void aztUnlockDoor(void)
-{
-#if AZT_ALLOW_TRAY_LOCK
-  aztSendCmd(ACMD_UNLOCK);
-  STEN_LOW;
-#endif
-  return;
-}
-
-/* 
- * Send a single command, return -1 on error, else 0
-*/
-static int aztSendCmd(int cmd)
-{  unsigned char data;
-   int retry;
-
-#ifdef AZT_DEBUG
-   printk("aztcd: Executing command %x\n",cmd);
-#endif
-   aztCmd=cmd;
-   outb(POLLED,MODE_PORT);
-   do { if (inb(STATUS_PORT)&AFL_STATUS) break;
-	inb(DATA_PORT);    /* if status left from last command, read and */
-      } while (1);         /* discard it */
-   do { if (inb(STATUS_PORT)&AFL_DATA) break;
-	inb(DATA_PORT);    /* if data left from last command, read and */
-      } while (1);         /* discard it */
-   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
-     { outb((unsigned char) cmd,CMD_PORT);
-       STEN_LOW;
-       data=inb(DATA_PORT);
-       if (data==AFL_OP_OK)
-	 { return 0;}           /*OP_OK?*/
-       if (data==AFL_OP_ERR)
-	 { STEN_LOW;
-	   data=inb(DATA_PORT);
-	   printk("### Error 1 aztcd: aztSendCmd %x  Error Code %x\n",cmd,data);
-	 }
-     }
-   if (retry>=AZT_RETRY_ATTEMPTS)
-     { printk("### Error 2 aztcd: aztSendCmd %x \n",cmd);
-       azt_error=0xA5;
-     }
-   RETURNM("aztSendCmd",-1);
-}
-
-/*
- * Send a play or read command to the drive, return -1 on error, else 0
-*/
-static int sendAztCmd(int cmd, struct azt_Play_msf *params)
-{  unsigned char data;
-   int retry;
-
-#ifdef AZT_DEBUG
-   printk("aztcd: play start=%02x:%02x:%02x  end=%02x:%02x:%02x\n", \
-	   params->start.min, params->start.sec, params->start.frame, \
-	   params->end.min,   params->end.sec,   params->end.frame);
-#endif   
-   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
-     { aztSendCmd(cmd);
-       outb(params -> start.min,CMD_PORT);
-       outb(params -> start.sec,CMD_PORT);
-       outb(params -> start.frame,CMD_PORT);
-       outb(params -> end.min,CMD_PORT);
-       outb(params -> end.sec,CMD_PORT);
-       outb(params -> end.frame,CMD_PORT);
-       STEN_LOW;
-       data=inb(DATA_PORT);
-       if (data==AFL_PA_OK)
-	 { return 0;}           /*PA_OK ?*/
-       if (data==AFL_PA_ERR)
-	 { STEN_LOW;
-	   data=inb(DATA_PORT);
-	   printk("### Error 1 aztcd: sendAztCmd %x  Error Code %x\n",cmd,data);
-	 }
-     }
-   if (retry>=AZT_RETRY_ATTEMPTS)
-     { printk("### Error 2 aztcd: sendAztCmd %x\n ",cmd);
-       azt_error=0xA5;
-     }
-   RETURNM("sendAztCmd",-1);
-}
-
-/*
- * Send a seek command to the drive, return -1 on error, else 0
-*/
-static int aztSeek(struct azt_Play_msf *params)
-{  unsigned char data;
-   int retry;
-
-#ifdef AZT_DEBUG
-   printk("aztcd: aztSeek %02x:%02x:%02x\n", \
-	   params->start.min, params->start.sec, params->start.frame);
-#endif   
-   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
-     { aztSendCmd(ACMD_SEEK);
-       outb(params -> start.min,CMD_PORT);
-       outb(params -> start.sec,CMD_PORT);
-       outb(params -> start.frame,CMD_PORT);
-       STEN_LOW;
-       data=inb(DATA_PORT);
-       if (data==AFL_PA_OK)
-	 { return 0;}           /*PA_OK ?*/
-       if (data==AFL_PA_ERR)
-	 { STEN_LOW;
-	   data=inb(DATA_PORT);
-	   printk("### Error 1 aztcd: aztSeek\n");
-	 }
-     }
-   if (retry>=AZT_RETRY_ATTEMPTS)
-     { printk("### Error 2 aztcd: aztSeek\n ");
-       azt_error=0xA5;
-     }
-   RETURNM("aztSeek",-1);
-}
-
-/* Send a Set Disk Type command
-   does not seem to work with Aztech drives, behavior is completely indepen-
-   dent on which mode is set ???
-*/
-static int aztSetDiskType(int type)
-{  unsigned char data;
-   int retry;
-
-#ifdef AZT_DEBUG
-   printk("aztcd: set disk type command: type= %i\n",type);
-#endif
-   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
-     { aztSendCmd(ACMD_SET_DISK_TYPE);
-       outb(type,CMD_PORT);
-       STEN_LOW;
-       data=inb(DATA_PORT);
-       if (data==AFL_PA_OK)     /*PA_OK ?*/
-	 { azt_read_mode=type;
-	   return 0;            
-	 }  
-       if (data==AFL_PA_ERR)
-	 { STEN_LOW;
-	   data=inb(DATA_PORT);
-	   printk("### Error 1 aztcd: aztSetDiskType %x Error Code %x\n",type,data);
-	 }
-     }
-   if (retry>=AZT_RETRY_ATTEMPTS)
-     { printk("### Error 2 aztcd: aztSetDiskType %x\n ",type);
-       azt_error=0xA5;
-     }
-   RETURNM("aztSetDiskType",-1);
-}
-
-
-/* 
- * Checking if the media has been changed not yet implemented
-*/
-static int check_aztcd_media_change(kdev_t full_dev)
-{ return 0;
-}
-
-
-/* used in azt_poll to poll the status, expects another program to issue a 
- * ACMD_GET_STATUS directly before 
- */
-static int aztStatus(void)  
-{       int st;
-/*	int i;
-
-	i = inb(STATUS_PORT) & AFL_STATUS;    is STEN=0?    ???
-	if (!i)
-*/      STEN_LOW;
-        if (aztTimeOutCount<AZT_TIMEOUT)	
-	{   	st = inb(DATA_PORT) & 0xFF;
-		return st;
-	}
-	else
-		RETURNM("aztStatus",-1);
-}
-
-/*
- * Get the drive status
- */
-static int getAztStatus(void)
-{       int st;
-
-	if (aztSendCmd(ACMD_GET_STATUS)) RETURNM("getAztStatus 1",-1);
-	STEN_LOW;
-	st = inb(DATA_PORT) & 0xFF;
-#ifdef AZT_DEBUG
-	printk("aztcd: Status = %x\n",st);
-#endif
-	if ((st == 0xFF)||(st&AST_CMD_CHECK))
-	 { printk("aztcd: AST_CMD_CHECK error or no status available\n");
-	   return -1;
-	 }
-
-	if (((st&AST_MODE_BITS)!=AST_BUSY) && (aztAudioStatus == CDROM_AUDIO_PLAY))
-	   /* XXX might be an error? look at q-channel? */
-	   aztAudioStatus = CDROM_AUDIO_COMPLETED;
-
-	if ((st & AST_DSK_CHG)||(st & AST_NOT_READY))
-	 { aztDiskChanged = 1;
-	   aztTocUpToDate = 0;
-	   aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-	 }
-	return st;
-}
-
-
-/*
- * Send a 'Play' command and get the status.  Use only from the top half.
- */
-static int aztPlay(struct azt_Play_msf *arg)
-{       if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) RETURNM("aztPlay",-1);
-	return 0;
-}
-
-
-long azt_msf2hsg(struct msf *mp)
-{ return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75
-		                  + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET;
-}
-
-static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
-{       int i, st;
-	struct azt_Toc qInfo;
-	struct cdrom_ti ti;
-	struct cdrom_tochdr tocHdr;
-	struct cdrom_msf msf;
-	struct cdrom_tocentry entry;
-	struct azt_Toc *tocPtr;            
-	struct cdrom_subchnl subchnl;
-        struct cdrom_volctrl volctrl;
-
-#ifdef AZT_DEBUG
-	printk("aztcd: starting aztcd_ioctl - Command:%x   Time: %li\n",cmd, jiffies);
-        printk("aztcd Status %x\n", getAztStatus());
-#endif
-	if (!ip) RETURNM("aztcd_ioctl 1",-EINVAL);
-	if (getAztStatus()<0) RETURNM("aztcd_ioctl 2", -EIO);
-	if ((!aztTocUpToDate)||(aztDiskChanged))
-	{ if ((i=aztUpdateToc())<0) RETURNM("aztcd_ioctl 3", i); /* error reading TOC */
-	}
-
-	switch (cmd)
-	{
-	case CDROMSTART:     /* Spin up the drive. Don't know, what to do,
-	                        at least close the tray */
-#if AZT_PRIVATE_IOCTLS 
-	        if (aztSendCmd(ACMD_CLOSE)) RETURNM("aztcd_ioctl 4",-1);
-	        STEN_LOW_WAIT;
-#endif
-		break;
-	case CDROMSTOP:      /* Spin down the drive */
-		if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 5",-1);
-		STEN_LOW_WAIT;
-		/* should we do anything if it fails? */
-		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		break;
-	case CDROMPAUSE:     /* Pause the drive */
-                if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL; 
-
-		if (aztGetQChannelInfo(&qInfo) < 0)
-		{ /* didn't get q channel info */
-		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		  RETURNM("aztcd_ioctl 7",0);
-		}
-		azt_Play.start = qInfo.diskTime;        /* remember restart point */
-
-		if (aztSendCmd(ACMD_PAUSE)) RETURNM("aztcd_ioctl 8",-1);
-		STEN_LOW_WAIT;
-		aztAudioStatus = CDROM_AUDIO_PAUSED;
-		break;
-	case CDROMRESUME:    /* Play it again, Sam */
-		if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL;
-		/* restart the drive at the saved position. */
-		i = aztPlay(&azt_Play);
-		if (i < 0)
-		{ aztAudioStatus = CDROM_AUDIO_ERROR;
-		  return -EIO;
-		}
-		aztAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-	case CDROMMULTISESSION: /*multisession support -- experimental*/
-	        { struct cdrom_multisession ms;
-#ifdef AZT_DEBUG
-   		  printk("aztcd ioctl MULTISESSION\n");
-#endif
-		  st = verify_area(VERIFY_READ, (void*) arg, sizeof(struct cdrom_multisession));
-		  if (st) return st;
-		  memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession));
-		  if (ms.addr_format == CDROM_MSF) 
-		     { ms.addr.msf.minute = azt_bcd2bin(DiskInfo.lastSession.min);
-		       ms.addr.msf.second = azt_bcd2bin(DiskInfo.lastSession.sec);
-		       ms.addr.msf.frame  = azt_bcd2bin(DiskInfo.lastSession.frame);
-		     } 
-		  else if (ms.addr_format == CDROM_LBA)
-		       ms.addr.lba = azt_msf2hsg(&DiskInfo.lastSession);
-		  else
-		       return -EINVAL;
-		  ms.xa_flag = DiskInfo.xa;
- 		  st = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct cdrom_multisession));
-		  if (st) return st;
-	  	  memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
-#ifdef AZT_DEBUG 
- 		  if (ms.addr_format == CDROM_MSF) 
-                      printk("aztcd multisession xa:%d, msf:%02x:%02x.%02x [%02x:%02x.%02x])\n",
-			      ms.xa_flag, ms.addr.msf.minute, ms.addr.msf.second, 
-			      ms.addr.msf.frame, DiskInfo.lastSession.min,
-			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
-		  else
-		      printk("atzcd multisession %d, lba:0x%08x [%02x:%02x.%02x])\n",
-			      ms.xa_flag, ms.addr.lba, DiskInfo.lastSession.min,
-			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
-#endif
-   		  return 0;
-		}
-	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
-		if (st) return st;
-		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
-		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;
-		azt_Play.start = Toc[ti.cdti_trk0].diskTime;
-		azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
-#ifdef AZT_DEBUG
-printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
-	azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
-	azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
-#endif
-		i = aztPlay(&azt_Play);
-		if (i < 0)
-		{ aztAudioStatus = CDROM_AUDIO_ERROR;
-		  return -EIO;
-		}
-		aztAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
-/*              if (aztAudioStatus == CDROM_AUDIO_PLAY) 
-		{ if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 9",-1);
-		  STEN_LOW;
-		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		}
-*/
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
-		if (st) return st;
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		/* convert to bcd */
-		azt_bin2bcd(&msf.cdmsf_min0);
-		azt_bin2bcd(&msf.cdmsf_sec0);
-		azt_bin2bcd(&msf.cdmsf_frame0);
-		azt_bin2bcd(&msf.cdmsf_min1);
-		azt_bin2bcd(&msf.cdmsf_sec1);
-		azt_bin2bcd(&msf.cdmsf_frame1);
-		azt_Play.start.min = msf.cdmsf_min0;
-		azt_Play.start.sec = msf.cdmsf_sec0;
-		azt_Play.start.frame = msf.cdmsf_frame0;
-		azt_Play.end.min = msf.cdmsf_min1;
-		azt_Play.end.sec = msf.cdmsf_sec1;
-		azt_Play.end.frame = msf.cdmsf_frame1;
-#ifdef AZT_DEBUG
-printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
-azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
-azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
-#endif
-		i = aztPlay(&azt_Play);
-		if (i < 0)
-		{ aztAudioStatus = CDROM_AUDIO_ERROR;
-		  return -EIO;
-		}
-		aztAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-
-	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;
-		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
-		break;
-	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof entry);
-		if (st) return st;
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
-		if (st) return st;
-		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
-		if ((!aztTocUpToDate)||aztDiskChanged) aztUpdateToc();
-		if (entry.cdte_track == CDROM_LEADOUT)
-		  tocPtr = &Toc[DiskInfo.last + 1];   /* ??? */
-		else if (entry.cdte_track > DiskInfo.last
-				|| entry.cdte_track < DiskInfo.first)
-		{ return -EINVAL;
-		}
-		else 
-		  tocPtr = &Toc[entry.cdte_track];
-		entry.cdte_adr = tocPtr -> ctrl_addr;
-		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
-		if (entry.cdte_format == CDROM_LBA)
-		  entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime);
-		else if (entry.cdte_format == CDROM_MSF)
-		{ entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min);
-		  entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec);
-		  entry.cdte_addr.msf.frame  = azt_bcd2bin(tocPtr -> diskTime.frame);
-		}
-		else
-		{ return -EINVAL;
-		}
-		memcpy_tofs((void *) arg, &entry, sizeof entry);
-		break;
-	case CDROMSUBCHNL:   /* Get subchannel info */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st) { 
-#ifdef AZT_DEBUG
-		          printk("aztcd: exiting aztcd_ioctl - Error 1 - Command:%x\n",cmd);
-#endif
-		          return st;
-		        }  
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st) { 
-#ifdef AZT_DEBUG
-		          printk("aztcd: exiting aztcd_ioctl - Error 2 - Command:%x\n",cmd);
-#endif
-		          return st;
-		        }  
-		memcpy_fromfs(&subchnl, (void *) arg, sizeof (struct cdrom_subchnl));
-		if (aztGetQChannelInfo(&qInfo) < 0)
-		if (st) { 
-#ifdef AZT_DEBUG
-		          printk("aztcd: exiting aztcd_ioctl - Error 3 - Command:%x\n",cmd);
-#endif
-     		  	  return -EIO;
-     		  	}  
-		subchnl.cdsc_audiostatus = aztAudioStatus;
-		subchnl.cdsc_adr = qInfo.ctrl_addr;
-		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
-		subchnl.cdsc_trk = azt_bcd2bin(qInfo.track);
-		subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex);
-		if (subchnl.cdsc_format == CDROM_LBA)
-		{ subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime);
-		  subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime);
-		}
-		else  /*default*/
-		{ subchnl.cdsc_format = CDROM_MSF;
-		  subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min);
-		  subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec);
-		  subchnl.cdsc_absaddr.msf.frame  = azt_bcd2bin(qInfo.diskTime.frame);
-		  subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min);
-		  subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec);
-		  subchnl.cdsc_reladdr.msf.frame  = azt_bcd2bin(qInfo.trackTime.frame);
-		}
-		memcpy_tofs((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
-		break;
-	case CDROMVOLCTRL:   /* Volume control 
-	 * With my Aztech CD268-01A volume control does not work, I can only
-	   turn the channels on (any value !=0) or off (value==0). Maybe it
-           works better with your drive */
-                st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl));
-                if (st) return (st);
-                memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
-		azt_Play.start.min = 0x21;
-		azt_Play.start.sec = 0x84;
-		azt_Play.start.frame = volctrl.channel0;
-		azt_Play.end.min =     volctrl.channel1;
-		azt_Play.end.sec =     volctrl.channel2;
-		azt_Play.end.frame =   volctrl.channel3;
-                sendAztCmd(ACMD_SET_VOLUME, &azt_Play);
-                STEN_LOW_WAIT;
-                break;
-	case CDROMEJECT:
-                aztUnlockDoor(); /* Assume user knows what they're doing */
-	       /* all drives can at least stop! */
-		if (aztAudioStatus == CDROM_AUDIO_PLAY) 
-		{ if (aztSendCmd(ACMD_STOP)) RETURNM("azt_ioctl 10",-1);
-		  STEN_LOW_WAIT;
-		}
-		if (aztSendCmd(ACMD_EJECT)) RETURNM("azt_ioctl 11",-1);
-		STEN_LOW_WAIT; /*???*/
-		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		break;
-	case CDROMEJECT_SW:
-		azt_auto_eject = (char) arg;
-		break;	
-        case CDROMRESET:
-        	outb(ACMD_SOFT_RESET,CMD_PORT);   /*send reset*/
-	        STEN_LOW;
-	        if (inb(DATA_PORT)!=AFL_OP_OK)    /*OP_OK?*/
-	          { printk("aztcd: AZTECH CD-ROM drive does not respond\n");
-                  }
-                break;
-/*Take care, the following code is not compatible with other CD-ROM drivers,
-  use it at your own risk with cdplay.c. Set AZT_PRIVATE_IOCTLS to 0 in aztcd.h,
-  if you do not want to use it!
-*/                  
-#if AZT_PRIVATE_IOCTLS 
-	case CDROMREADCOOKED: /*read data in mode 1 (2048 Bytes)*/
-	case CDROMREADRAW:    /*read data in mode 2 (2336 Bytes)*/
-		{ st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
-		  if (st) return st;
-		  st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf);
-		  if (st) return st;
-		  memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		  /* convert to bcd */
-		  azt_bin2bcd(&msf.cdmsf_min0);
-		  azt_bin2bcd(&msf.cdmsf_sec0);
-		  azt_bin2bcd(&msf.cdmsf_frame0);
-		  msf.cdmsf_min1=0;
-		  msf.cdmsf_sec1=0;
-		  msf.cdmsf_frame1=1; /*read only one frame*/
-		  azt_Play.start.min = msf.cdmsf_min0;
-		  azt_Play.start.sec = msf.cdmsf_sec0;
-		  azt_Play.start.frame = msf.cdmsf_frame0;
-		  azt_Play.end.min = msf.cdmsf_min1;
-		  azt_Play.end.sec = msf.cdmsf_sec1;
-		  azt_Play.end.frame = msf.cdmsf_frame1;
-		  if (cmd==CDROMREADRAW)
-		  { if (DiskInfo.xa)
-		       { return -1;         /*XA Disks can't be read raw*/
-		       }
-		    else   
-		       { if (sendAztCmd(ACMD_PLAY_READ_RAW, &azt_Play)) return -1;
-		         DTEN_LOW;
-		         insb(DATA_PORT,buf,CD_FRAMESIZE_RAW);
-		         memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE_RAW);
-		       }  
-		  }
-		  else /*CDROMREADCOOKED*/
-		  { if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
-		    DTEN_LOW;
-		    insb(DATA_PORT,buf,CD_FRAMESIZE);
-		    memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE);
-		  }
-		 } 
-                break;
-	case CDROMSEEK:    /*seek msf address*/
-		st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
-		if (st) return st;
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		/* convert to bcd */
-		azt_bin2bcd(&msf.cdmsf_min0);
-		azt_bin2bcd(&msf.cdmsf_sec0);
-		azt_bin2bcd(&msf.cdmsf_frame0);
-		azt_Play.start.min = msf.cdmsf_min0;
-		azt_Play.start.sec = msf.cdmsf_sec0;
-		azt_Play.start.frame = msf.cdmsf_frame0;
-		if (aztSeek(&azt_Play)) return -1;
-                break;
-#endif /*end of incompatible code*/       
-	case CDROMREADMODE1: /*set read data in mode 1*/
-                return aztSetDiskType(AZT_MODE_1);
-	case CDROMREADMODE2: /*set read data in mode 2*/
-                return aztSetDiskType(AZT_MODE_2);	    
-	default:
-		return -EINVAL;
-	}
-#ifdef AZT_DEBUG
-        printk("aztcd: exiting aztcd_ioctl Command:%x  Time:%li\n",cmd,jiffies);
-#endif
-	return 0;
-}
-
-
-/*
- * Take care of the different block sizes between cdrom and Linux.
- * When Linux gets variable block sizes this will probably go away.
- */
-static void azt_transfer(void)
-{ 
-#ifdef AZT_TEST
-  printk("aztcd: executing azt_transfer Time:%li\n",jiffies);
-#endif
-  if (CURRENT_VALID) {
-    while (CURRENT -> nr_sectors) {
-      int bn = CURRENT -> sector / 4;
-      int i;
-      for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i)
-	;
-      if (i < AZT_BUF_SIZ) {
-	int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
-	int nr_sectors = 4 - (CURRENT -> sector & 3);
-	if (azt_buf_out != i) {
-	  azt_buf_out = i;
-	  if (azt_buf_bn[i] != bn) {
-	    azt_buf_out = -1;
-	    continue;
-	  }
-	}
-	if (nr_sectors > CURRENT -> nr_sectors)
-	  nr_sectors = CURRENT -> nr_sectors;
-	memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512);
-	CURRENT -> nr_sectors -= nr_sectors;
-	CURRENT -> sector += nr_sectors;
-	CURRENT -> buffer += nr_sectors * 512;
-      } else {
-	azt_buf_out = -1;
-	break;
-      }
-    }
-  }
-}
-
-
-static void do_aztcd_request(void)
-{
-#ifdef AZT_TEST
-  printk(" do_aztcd_request(%ld+%ld) Time:%li\n", CURRENT -> sector, CURRENT -> nr_sectors,jiffies);
-#endif
-  if (DiskInfo.audio) 
-    { printk("aztcd: Error, tried to mount an Audio CD\n");
-      end_request(0);
-      return;
-    }
-  azt_transfer_is_active = 1;
-  while (CURRENT_VALID) {
-    if (CURRENT->bh) {
-      if (!CURRENT->bh->b_lock)
-	panic(DEVICE_NAME ": block not locked");
-    }
-    azt_transfer();
-    if (CURRENT -> nr_sectors == 0) {
-      end_request(1);
-    } else {
-      azt_buf_out = -1;         /* Want to read a block not in buffer */
-      if (azt_state == AZT_S_IDLE) {
-	if ((!aztTocUpToDate)||aztDiskChanged) {
-	  if (aztUpdateToc() < 0) {
-	    while (CURRENT_VALID)
-	      end_request(0);
-	    break;
-	  }
-	}
-	azt_state = AZT_S_START;
-	AztTries = 5;
-	SET_TIMER(azt_poll, HZ/100);
-      }
-      break;
-    }
-  }
-  azt_transfer_is_active = 0;
-#ifdef AZT_TEST2
-  printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
-	  azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-  printk(" do_aztcd_request ends  Time:%li\n",jiffies);
-#endif
-}
-
-static void azt_poll(void)
-{
-    int st = 0;
-    int loop_ctl = 1;
-    int skip = 0;
-
-    if (azt_error) {                             /* ???*/
-	if (aztSendCmd(ACMD_GET_ERROR)) RETURN("azt_poll 1");
-	STEN_LOW;
-	azt_error=inb(DATA_PORT)&0xFF;
-	printk("aztcd: I/O error 0x%02x\n", azt_error);
-	azt_invalidate_buffers();
-#ifdef WARN_IF_READ_FAILURE
-	if (AztTries == 5)
-	  printk("aztcd: Read of Block %d Failed - Maybe Audio Disk?\n", azt_next_bn);
-#endif
-	if (!AztTries--) {
-	  printk("aztcd: Read of Block %d Failed, Maybe Audio Disk? Giving up\n", azt_next_bn);
-	  if (azt_transfer_is_active) {
-	    AztTries = 0;
-	    loop_ctl = 0;
-	  }
-	  if (CURRENT_VALID)
-	    end_request(0);
-	  AztTries = 5;
-	}
-    azt_error = 0;
-    azt_state = AZT_S_STOP;
-    }
-
-    while (loop_ctl)
-    {
-      loop_ctl = 0;   /* each case must flip this back to 1 if we want
-			 to come back up here */
-      switch (azt_state) {
-
-	case AZT_S_IDLE:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_IDLE\n");
-	    }
-#endif
-	  return;
-
-	case AZT_S_START:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_START\n");
-	  }
-#endif
-	  if(aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 2");  /*result will be checked by aztStatus() */
-	  azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE;
-	  AztTimeout = 3000;
-	  break;
-
-	case AZT_S_MODE:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_MODE\n");
-	  }
-#endif
-	  if (!skip) {
-	    if ((st = aztStatus()) != -1) {
-	      if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
-		aztDiskChanged = 1;
-		aztTocUpToDate = 0;
-		azt_invalidate_buffers();
-		end_request(0);
-		printk("aztcd: Disk Changed or Not Ready 1 - Unmount Disk!\n");
-	      }
-	    } else break;
-	  }
-	  skip = 0;
-
-	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
-	    aztDiskChanged = 1;
-	    aztTocUpToDate = 0;
-            printk("aztcd: Disk Changed or Not Ready 2 - Unmount Disk!\n");
-            end_request(0);
-	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
-	    if (azt_transfer_is_active) {
-	      azt_state = AZT_S_START;
-	      loop_ctl = 1;   /* goto immediately */
-	      break;
-	    }
-	    azt_state = AZT_S_IDLE;
-	    while (CURRENT_VALID)
-	      end_request(0);
-	    return;
-	  }
-					/*???*/
-/*	  if (aztSendCmd(ACMD_SET_MODE)) RETURN("azt_poll 3");
-	  outb(0x01, DATA_PORT);          
-	  PA_OK;
-	  STEN_LOW;
-*/        if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 4");
-	  STEN_LOW; /*???*/
-	  azt_mode = 1;
-	  azt_state = AZT_S_READ;
-	  AztTimeout = 3000;
-
-	  break;
-
-
-	case AZT_S_READ:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old)  {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_READ\n");
-	  }
-#endif
-	  if (!skip) {
-	      if ((st = aztStatus()) != -1) {
-		if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
-		aztDiskChanged = 1;
-		aztTocUpToDate = 0;
-		azt_invalidate_buffers();
-		printk("aztcd: Disk Changed or Not Ready 3 - Unmount Disk!\n");
-                end_request(0);               
-		}
-	      } else break;
-	  } 
-	    
-	  skip = 0;
-	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
-	    aztDiskChanged = 1;
-	    aztTocUpToDate = 0;
-	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
-	    if (azt_transfer_is_active) {
-	      azt_state = AZT_S_START;
-	      loop_ctl = 1;
-	      break;
-	    }
-	    azt_state = AZT_S_IDLE;
-	    while (CURRENT_VALID)
-	    end_request(0);
-	    return;
-	  }
-
-	  if (CURRENT_VALID) {
-	    struct azt_Play_msf msf;
-	    azt_next_bn = CURRENT -> sector / 4;
-	    azt_hsg2msf(azt_next_bn, &msf.start);
-	    azt_read_count=AZT_BUF_SIZ;    /*??? fast, because we read ahead*/
-/*          azt_read_count= CURRENT->nr_sectors;      slow
-*/
-	    msf.end.min = 0;
-	    msf.end.sec = 0;            
-	    msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
-#ifdef AZT_TEST3
-	    printk("---reading msf-address %x:%x:%x  %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame);
-	    printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
-		    azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-#endif 
-            if (azt_read_mode==AZT_MODE_2)
-               { sendAztCmd(ACMD_PLAY_READ_RAW, &msf); /*XA disks in raw mode*/
-               }
-            else
-               { sendAztCmd(ACMD_PLAY_READ, &msf);     /*others in cooked mode*/
-               }
-	    azt_state = AZT_S_DATA;
-	    AztTimeout = READ_TIMEOUT;
-	  } else {
-	    azt_state = AZT_S_STOP;
-	    loop_ctl = 1;
-	    break;
-	  }
-
-	  break;
-
-
-	case AZT_S_DATA:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old)  {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_DATA\n");
-	  }
-#endif
-
-	  st = inb(STATUS_PORT) & AFL_STATUSorDATA;   /*???*/
-
-	  switch (st) {
-
-	    case AFL_DATA:
-#ifdef AZT_TEST3
-	      if (st!=azt_st_old)  {
-		azt_st_old=st; 
-		printk("---AFL_DATA st:%x\n",st);
-	      }
-#endif
-	      if (!AztTries--) {
-		printk("aztcd: Read of Block %d Failed, Maybe Audio Disk ? Giving up\n", azt_next_bn);
-		if (azt_transfer_is_active) {
-		  AztTries = 0;
-		  break;
-		}
-		if (CURRENT_VALID)
-		  end_request(0);
-		AztTries = 5;
-	      }
-	      azt_state = AZT_S_START;
-	      AztTimeout = READ_TIMEOUT;
-	      loop_ctl = 1;
-	      break;
-
-	    case AFL_STATUSorDATA:
-#ifdef AZT_TEST3
-	      if (st!=azt_st_old)  {
-		azt_st_old=st;
-		printk("---AFL_STATUSorDATA st:%x\n",st);
-	      }
-#endif
-	      break;
-
-	    default:
-#ifdef AZT_TEST3
-	      if (st!=azt_st_old)  {
-		azt_st_old=st;
-		printk("---default: st:%x\n",st);
-	      }
-#endif
-	      AztTries = 5;
-	      if (!CURRENT_VALID && azt_buf_in == azt_buf_out) {
-		azt_state = AZT_S_STOP;
-		loop_ctl = 1;
-		break;
-	      }
-	      if (azt_read_count<=0)
-		printk("aztcd: warning - try to read 0 frames\n");
-	      while (azt_read_count)      /*??? fast read ahead loop*/
-	       { azt_buf_bn[azt_buf_in] = -1;
-		 DTEN_LOW;                      /*??? unsolved problem, very
-						      seldom we get timeouts
-						      here, don't now the real
-						      reason. With my drive this
-						      sometimes also happens with
-						      Aztech's original driver under
-						      DOS. Is it a hardware bug? 
-						      I tried to recover from such
-						      situations here. Zimmermann*/
-		 if (aztTimeOutCount>=AZT_TIMEOUT) 
-		  { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in);
-		    printk("azt_transfer_is_active:%x\n",azt_transfer_is_active);
-		    azt_read_count=0;
-		    azt_state = AZT_S_STOP;
-		    loop_ctl = 1;
-		    end_request(1);  /*should we have here (1) or (0)? */
-		  }
-		 else
-		  { if (azt_read_mode==AZT_MODE_2)
-		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE_RAW * azt_buf_in, CD_FRAMESIZE_RAW);
-		       }
-		    else
-		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE * azt_buf_in, CD_FRAMESIZE);
-                       }
-		    azt_read_count--;
-#ifdef AZT_TEST3
-		    printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count);
-		    printk("azt_next_bn:%d  azt_buf_in:%d azt_buf_out:%d  azt_buf_bn:%d\n", \
-			 azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-#endif
-		    azt_buf_bn[azt_buf_in] = azt_next_bn++;
-		    if (azt_buf_out == -1)
-		      azt_buf_out = azt_buf_in;
-		    azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1;
-		  }
-	       }
-	      if (!azt_transfer_is_active) {
-		while (CURRENT_VALID) {
-		  azt_transfer();
-		  if (CURRENT -> nr_sectors == 0)
-		    end_request(1);
-		  else
-		    break;
-		}
-	      }
-
-	      if (CURRENT_VALID
-		&& (CURRENT -> sector / 4 < azt_next_bn ||
-		CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) {
-		azt_state = AZT_S_STOP;
-		loop_ctl = 1;
-		break;
-	      }
-	      AztTimeout = READ_TIMEOUT;   
-	      if (azt_read_count==0) {
-		azt_state = AZT_S_STOP;   /*???*/
-		loop_ctl = 1;
-		break;           
-	      } 
-	      break;
-	    }
-    break;
-
-
-	case AZT_S_STOP:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_STOP\n");
-	  }
-#endif
-	  if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count);  /*???*/
-	  while (azt_read_count!=0) {
-	    int i;
-	    if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
-	      if (azt_read_mode==AZT_MODE_2)
-	         for (i=0; i<CD_FRAMESIZE_RAW; i++) inb(DATA_PORT);
-	      else   
-	         for (i=0; i<CD_FRAMESIZE; i++) inb(DATA_PORT);
-	    }
-	    azt_read_count--;
-	  }  
-	  if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 5");
-	  azt_state = AZT_S_STOPPING;
-	  AztTimeout = 1000;
-	  break;
-
-	case AZT_S_STOPPING:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_STOPPING\n");
-	  }
-#endif
-
-	  if ((st = aztStatus()) == -1 && AztTimeout)
-	    break;
-
-	  if ((st != -1) && ((st & AST_DSK_CHG)||(st & AST_NOT_READY))) {
-	    aztDiskChanged = 1;
-	    aztTocUpToDate = 0;
-	    azt_invalidate_buffers();
-	    printk("aztcd: Disk Changed or Not Ready 4 - Unmount Disk!\n");
-	    end_request(0);
-	  }
-
-
-#ifdef AZT_TEST3
-	  printk("CURRENT_VALID %d azt_mode %d\n",
-	     CURRENT_VALID, azt_mode);
-#endif
-
-	  if (CURRENT_VALID) {
-	    if (st != -1) {
-	      if (azt_mode == 1) {
-		azt_state = AZT_S_READ;
-		loop_ctl = 1;
-		skip = 1;
-		break;
-	      } else {
-		azt_state = AZT_S_MODE;
-		loop_ctl = 1;
-		skip = 1;
-		break;
-	      }
-	    } else {
-	      azt_state = AZT_S_START;
-	      AztTimeout = 1;
-	    }
-	  } else {
-	    azt_state = AZT_S_IDLE;
-	    return;
-	  }
-	  break;
-
-	default:
-	  printk("aztcd: invalid state %d\n", azt_state);
-	  return;
-      }  /* case */
-    } /* while */
-  
-
-   if (!AztTimeout--) 
-    { printk("aztcd: timeout in state %d\n", azt_state);
-      azt_state = AZT_S_STOP;
-      if (aztSendCmd(ACMD_STOP)) RETURN("azt_poll 6"); 
-      STEN_LOW_WAIT;     
-    };
-
-  SET_TIMER(azt_poll, HZ/100);
-}
-
-static void azt_invalidate_buffers(void)
-{ int i;
-
-#ifdef AZT_DEBUG
-  printk("aztcd: executing azt_invalidate_buffers\n");
-#endif
-  for (i = 0; i < AZT_BUF_SIZ; ++i)
-    azt_buf_bn[i] = -1;
-  azt_buf_out = -1;
-}
-
-/*
- * Open the device special file.  Check that a disk is in.
- */
-int aztcd_open(struct inode *ip, struct file *fp)
-{       int st;
-
-#ifdef AZT_DEBUG
-	printk("aztcd: starting aztcd_open\n");
-#endif
-	if (aztPresent == 0)
-		return -ENXIO;                  /* no hardware */
-
-	if (!azt_open_count && azt_state == AZT_S_IDLE) 
-	  { azt_invalidate_buffers();
-
-	    st = getAztStatus();                    /* check drive status */
-	    if (st == -1) return -EIO;              /* drive doesn't respond */
-
-            if (st & AST_DOOR_OPEN)
-               { /* close door, then get the status again. */
-	         printk("aztcd: Door Open?\n");
-                 aztCloseDoor();     
-                 st = getAztStatus();
-               }        
-
-	    if ((st & AST_NOT_READY) || (st & AST_DSK_CHG)) /*no disk in drive or changed*/
-	       { printk("aztcd: Disk Changed or No Disk in Drive?\n");
-                 aztTocUpToDate=0;
-               }
-            if (aztUpdateToc())	return -EIO;
-	       
-	  }
-	++azt_open_count;
-        MOD_INC_USE_COUNT;
-	aztLockDoor();
-
-
-#ifdef AZT_DEBUG
-	printk("aztcd: exiting aztcd_open\n");
-#endif
-	return 0;
-}
-
-
-/*
- * On close, we flush all azt blocks from the buffer cache.
- */
-static void aztcd_release(struct inode * inode, struct file * file)
-{ 
-#ifdef AZT_DEBUG
-  printk("aztcd: executing aztcd_release\n");
-  printk("inode: %p, inode->i_rdev: %x    file: %p\n",inode,inode->i_rdev,file);
-#endif
-  MOD_DEC_USE_COUNT;
-  if (!--azt_open_count) {
-	azt_invalidate_buffers();
-	sync_dev(inode->i_rdev);             /*??? isn't it a read only dev?*/
-	invalidate_buffers(inode -> i_rdev);
-	aztUnlockDoor();
-        if (azt_auto_eject)
-           aztSendCmd(ACMD_EJECT);
-        CLEAR_TIMER;
-  }
-  return;
-}
-
-
-static struct file_operations azt_fops = {
-	NULL,                   /* lseek - default */
-	block_read,             /* read - general block-dev read */
-	block_write,            /* write - general block-dev write */
-	NULL,                   /* readdir - bad */
-	NULL,                   /* select */
-	aztcd_ioctl,            /* ioctl */
-	NULL,                   /* mmap */
-	aztcd_open,             /* open */
-	aztcd_release,          /* release */
-	NULL,                   /* fsync */
-	NULL,                   /* fasync*/
-	check_aztcd_media_change, /*media change*/
-	NULL                    /* revalidate*/
-};
-
-/*
- * Test for presence of drive and initialize it.  Called at boot time.
- */
-
-int aztcd_init(void)
-{       long int count, max_count;
-	unsigned char result[50];
-	int st;
-
-	if (azt_port <= 0) {
-	  printk("aztcd: no Aztech CD-ROM Initialization");
-          return -EIO;
-	}
-	printk("aztcd: Aztech, Orchid, Okano, Wearnes CD-ROM Driver (C) 1994,1995 W.Zimmermann\n");
-	printk("aztcd: DriverVersion=%s  BaseAddress=0x%x \n",AZT_VERSION,azt_port);
-
-	if (check_region(azt_port, 4)) {
-	  printk("aztcd: conflict, I/O port (%X) already used\n",
-		 azt_port);
-          return -EIO;
-	}
-
-#ifdef AZT_SW32   /*CDROM connected to Soundwave32 card*/
-        if ((0xFF00 & inw(AZT_SW32_ID_REG)) != 0x4500)
-           { printk("aztcd: no Soundwave32 card detected at base:%x init:%x config:%x id:%x\n",
-		 AZT_SW32_BASE_ADDR,AZT_SW32_INIT,AZT_SW32_CONFIG_REG,AZT_SW32_ID_REG);
-                 return -EIO;
-	   }
-        else                
-           { printk("aztcd: Soundwave32 card detected at %x  Version %x\n",
-	 	 AZT_SW32_BASE_ADDR, inw(AZT_SW32_ID_REG));
-	     outw(AZT_SW32_INIT,AZT_SW32_CONFIG_REG);
-	     for (count=0;count<10000;count++);          /*delay a bit*/    	 
-           }
-#endif	
-
-	/* check for presence of drive */
-	outb(POLLED,MODE_PORT);                 /*???*/
-	inb(CMD_PORT);
-	inb(CMD_PORT);
-	outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/
-
-/*	STEN_LOW  - special implementation for drive recognition
-*/      aztTimeOutCount=0;   
-	do { aztIndatum=inb(STATUS_PORT);
-	     aztTimeOutCount++; 
-	     if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; 
-	   } while (aztIndatum&AFL_STATUS); 
-
-	if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/
-           { 
-#ifndef MODULE
-             if (azt_cont!=0x79)   
-	        { printk("aztcd: no AZTECH CD-ROM drive found-Try boot parameter aztcd=<BaseAddress>,0x79\n");
-	          return -EIO;
-                }
-#else        
-             if (0)
-                {
-                }
-#endif	     
-             else   
-       	        { printk("aztcd: drive reset - please wait\n");
-	          for (count=0;count<50;count++)
-	            { inb(STATUS_PORT);    /*removing all data from earlier tries*/
-	              inb(DATA_PORT);
-	            }
-	          outb(POLLED,MODE_PORT);           /*???*/
-	          inb(CMD_PORT);
-	          inb(CMD_PORT);
-		  getAztStatus(); 		    /*trap errors*/
-	          outb(ACMD_SOFT_RESET,CMD_PORT);   /*send reset*/
-	          STEN_LOW;
-	          if (inb(DATA_PORT)!=AFL_OP_OK)    /*OP_OK?*/
-	             { printk("aztcd: no AZTECH CD-ROM drive found\n");
-                       return -EIO;
-	             } 
-	          for (count = 0; count < AZT_TIMEOUT; count++); 
-	             { count=count*2;          /* delay a bit */
-	               count=count/2;
-	             }                        
-	          if ((st=getAztStatus())==-1)
-	             { printk("aztcd: Drive Status Error Status=%x\n",st);
-                       return -EIO;
-	             }
-#ifdef AZT_DEBUG
-	          printk("aztcd: Status = %x\n",st);
-#endif
-	          outb(POLLED,MODE_PORT);              /*???*/
-	          inb(CMD_PORT);
-	          inb(CMD_PORT);
-	          outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/
-	          STEN_LOW;
-	          OP_OK;
-	        } 
-           }
-	azt_init_end=1;
-	STEN_LOW;
-	result[0]=inb(DATA_PORT);        /*reading in a null byte???*/
-	for (count=1;count<50;count++)   /*Reading version string*/
-	 { aztTimeOutCount=0;            /*here we must implement STEN_LOW differently*/
-	   do { aztIndatum=inb(STATUS_PORT);/*because we want to exit by timeout*/
-		aztTimeOutCount++; 
-		if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; 
-	      } while (aztIndatum&AFL_STATUS); 
-	   if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break;  /*all chars read?*/
-	   result[count]=inb(DATA_PORT);
-	 }
-	if (count>30) max_count=30;  /*print max.30 chars of the version string*/
-	else          max_count=count;
-	printk("aztcd: FirmwareVersion=");
-	for (count=1;count<max_count;count++) printk("%c",result[count]);
-	printk("<<<\n");
-
-	if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T'))
-	 { printk("aztcd: AZTECH drive detected\n"); /*AZTECH*/    
-	 }
-	else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D'))
-	 { printk("aztcd: ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
-	 }
-	else                                               /*OTHERS or none*/
-	 { printk("aztcd: : unknown drive or firmware version detected\n");
-	   printk("                      azt may not run stable, if you want to try anyhow,\n");
-	   printk("                      boot with: aztcd=<BaseAddress>,0x79\n");
-	   if ((azt_cont!=0x79))     
-	     { printk("aztcd: FirmwareVersion=");
-	       for (count=1;count<5;count++) printk("%c",result[count]);
-	       printk("\n");
-	       printk("aztcd: Aborted\n");
-               return -EIO;
-	     }
-	 }
-	if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0)
-	{
-		printk("aztcd: Unable to get major %d for Aztech CD-ROM\n",
-		       MAJOR_NR);
-                return -EIO;
-	}
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = 4;
-
-	request_region(azt_port, 4, "aztcd");
-
-	azt_invalidate_buffers();
-	aztPresent = 1;
-	aztCloseDoor();
-	printk("aztcd: End Init\n");
-        return (0);
-}
-
-
-static void azt_hsg2msf(long hsg, struct msf *msf)
-{       hsg += 150;
-	msf -> min = hsg / 4500;
-	hsg %= 4500;
-	msf -> sec = hsg / 75;
-	msf -> frame = hsg % 75;
-#ifdef AZT_DEBUG
-	if (msf->min  >=70) printk("aztcd: Error hsg2msf address Minutes\n");
-	if (msf->sec  >=60) printk("aztcd: Error hsg2msf address Seconds\n");
-	if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n");
-#endif
-	azt_bin2bcd(&msf -> min);           /* convert to BCD */
-	azt_bin2bcd(&msf -> sec);
-	azt_bin2bcd(&msf -> frame);
-}
-
-
-static void azt_bin2bcd(unsigned char *p)
-{       int u, t;
-
-	u = *p % 10;
-	t = *p / 10;
-	*p = u | (t << 4);
-}
-
-static int azt_bcd2bin(unsigned char bcd)
-{       return (bcd >> 4) * 10 + (bcd & 0xF);
-}
-
-
-
-/*
- * Read a value from the drive.  Should return quickly, so a busy wait
- * is used to avoid excessive rescheduling. The read command itself must
- * be issued with aztSendCmd() directly before
- */
-static int aztGetValue(unsigned char *result)
-{       int s;
-
-	STEN_LOW;
-	if (aztTimeOutCount>=AZT_TIMEOUT)
-	{       printk("aztcd: aztGetValue timeout\n");
-		return -1;
-	}
-	s = inb(DATA_PORT) & 0xFF;
-	*result = (unsigned char) s;
-	return 0;
-}
-
-
-/*
- * Read the current Q-channel info.  Also used for reading the
- * table of contents.
- */
-int aztGetQChannelInfo(struct azt_Toc *qp)
-{       unsigned char notUsed;
-	int st;
-
-#ifdef AZT_DEBUG
-	printk("aztcd: starting aztGetQChannelInfo  Time:%li\n",jiffies);
-#endif
-	if ((st=getAztStatus())==-1)        RETURNM("aztGetQChannelInfo 1",-1);
-	if (aztSendCmd(ACMD_GET_Q_CHANNEL)) RETURNM("aztGetQChannelInfo 2",-1);
-	/*STEN_LOW_WAIT; ??? Dosemu0.60's cdrom.c does not like STEN_LOW_WAIT here*/
-	if (aztGetValue(&notUsed)) RETURNM("aztGetQChannelInfo 3",-1); /*??? Nullbyte einlesen*/
-	if ((st&AST_MODE_BITS)==AST_INITIAL)
-	 { qp->ctrl_addr=0;      /* when audio stop ACMD_GET_Q_CHANNEL returns */
-	   qp->track=0;          /* only one byte with Aztech drives */
-	   qp->pointIndex=0;
-	   qp->trackTime.min=0;
-	   qp->trackTime.sec=0;
-	   qp->trackTime.frame=0;
-	   qp->diskTime.min=0;
-	   qp->diskTime.sec=0;
-	   qp->diskTime.frame=0;
-	   return 0;  
-	 }
-	else
-	 { if (aztGetValue(&qp -> ctrl_addr) < 0)       RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> track) < 0)           RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> pointIndex) < 0)      RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> trackTime.min) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> trackTime.sec) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> trackTime.frame) < 0) RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&notUsed) < 0)               RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> diskTime.min) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> diskTime.sec) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> diskTime.frame) < 0)  RETURNM("aztGetQChannelInfo 4",-1);
-	 }
-#ifdef AZT_DEBUG
-	printk("aztcd: exiting aztGetQChannelInfo  Time:%li\n",jiffies);
-#endif
-	return 0;
-}
-
-/*
- * Read the table of contents (TOC) and TOC header if necessary
- */
-static int aztUpdateToc()
-{       int st;
-
-#ifdef AZT_DEBUG
-	printk("aztcd: starting aztUpdateToc  Time:%li\n",jiffies);
-#endif  
-	if (aztTocUpToDate)
-		return 0;
-
-	if (aztGetDiskInfo() < 0)
-		return -EIO;
-
-	if (aztGetToc(0) < 0)
-		return -EIO;
-
-        /*audio disk detection
-          with my Aztech drive there is no audio status bit, so I use the copy
-          protection bit of the first track. If this track is copy protected 
-          (copy bit = 0), I assume, it's an audio  disk. Strange, but works ??? */
-        if (!(Toc[DiskInfo.first].ctrl_addr & 0x40)) 
-           DiskInfo.audio=1;
-        else 
-           DiskInfo.audio=0;
-
-        /* XA detection */
-	if (! DiskInfo.audio) 
-	   { azt_Play.start.min   = 0;  /*XA detection only seems to work*/
-             azt_Play.start.sec   = 2;  /*when we play a track*/
-	     azt_Play.start.frame = 0;
-	     azt_Play.end.min     = 0;
-	     azt_Play.end.sec     = 0;
-	     azt_Play.end.frame   = 1;
-	     if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
-	     DTEN_LOW;
-	     for (st=0;st<CD_FRAMESIZE;st++) inb(DATA_PORT);
-           } 
-        DiskInfo.xa = getAztStatus() & AST_MODE;
-        if (DiskInfo.xa) 
-           { printk("aztcd: XA support experimental - mail results to zimmerma@rz.fht-esslingen.de\n");
-           }
-        
-        /*multisession detection
-          support for multisession CDs is done automatically with Aztech drives,
-          we don't have to take care about TOC redirection; if we want the isofs
-          to take care about redirection, we have to set AZT_MULTISESSION to 1*/
-        DiskInfo.multi=0;
-#if AZT_MULTISESSION
-  	if (DiskInfo.xa) 
-  	   { aztGetMultiDiskInfo(); /*here Disk.Info.multi is set*/
-  	   }
-#endif
-        if (DiskInfo.multi)
-           { DiskInfo.lastSession.min  = Toc[DiskInfo.next].diskTime.min;
-	     DiskInfo.lastSession.sec  = Toc[DiskInfo.next].diskTime.sec;
-             DiskInfo.lastSession.frame= Toc[DiskInfo.next].diskTime.frame;
-             printk("aztcd: Multisession support experimental\n");
-           }
-        else
-           { DiskInfo.lastSession.min  = Toc[DiskInfo.first].diskTime.min;
-	     DiskInfo.lastSession.sec  = Toc[DiskInfo.first].diskTime.sec;
-             DiskInfo.lastSession.frame= Toc[DiskInfo.first].diskTime.frame;
-           }
-
-	aztTocUpToDate = 1;
-#ifdef AZT_DEBUG
-	printk("aztcd: exiting aztUpdateToc  Time:%li\n",jiffies);
-#endif
-	return 0;
-}
-
-
-/* Read the table of contents header, i.e. no. of tracks and start of first 
- * track
- */
-static int aztGetDiskInfo()
-{ int limit;
-  unsigned char test;
-  struct azt_Toc qInfo;
-
-#ifdef AZT_DEBUG
-  printk("aztcd: starting aztGetDiskInfo  Time:%li\n",jiffies);
-#endif
-  if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetDiskInfo 1",-1);
-  STEN_LOW_WAIT;
-  test=0;
-  for (limit=300;limit>0;limit--)
-   {  if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetDiskInfo 2",-1);
-      if (qInfo.pointIndex==0xA0)   /*Number of FirstTrack*/
-	{ DiskInfo.first = qInfo.diskTime.min;
-	  DiskInfo.first = azt_bcd2bin(DiskInfo.first);
-	  test=test|0x01;
-	}
-      if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
-	{ DiskInfo.last  = qInfo.diskTime.min;
-	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
-	  test=test|0x02;
-	}
-      if (qInfo.pointIndex==0xA2)   /*DiskLength*/
-	{ DiskInfo.diskLength.min=qInfo.diskTime.min;
-	  DiskInfo.diskLength.sec=qInfo.diskTime.sec;
-	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
-	  test=test|0x04;
-	}
-      if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01))   /*StartTime of First Track*/
-	{ DiskInfo.firstTrack.min=qInfo.diskTime.min;
-	  DiskInfo.firstTrack.sec=qInfo.diskTime.sec;
-	  DiskInfo.firstTrack.frame=qInfo.diskTime.frame;
-	  test=test|0x08;
-	}
-      if (test==0x0F) break;
-   }
-#ifdef AZT_DEBUG
-  printk ("aztcd: exiting aztGetDiskInfo  Time:%li\n",jiffies);
-  printk("Disk Info: first %d last %d length %02X:%02X.%02X dez  first %02X:%02X.%02X dez\n",
-  	  DiskInfo.first,
-	  DiskInfo.last,
-	  DiskInfo.diskLength.min,
-	  DiskInfo.diskLength.sec,
-	  DiskInfo.diskLength.frame,
-	  DiskInfo.firstTrack.min,
-	  DiskInfo.firstTrack.sec,
-	  DiskInfo.firstTrack.frame);
-#endif
-  if (test!=0x0F) return -1;
-  return 0;
-}
-
-#if AZT_MULTISESSION
-/*
- * Get Multisession Disk Info
- */
-static int aztGetMultiDiskInfo(void)
-{ int limit, k=5;
-  unsigned char test;
-  struct azt_Toc qInfo;
-
-#ifdef AZT_DEBUG
-  printk("aztcd: starting aztGetMultiDiskInfo\n");
-#endif
-
-  do { azt_Play.start.min   = Toc[DiskInfo.last+1].diskTime.min;
-       azt_Play.start.sec   = Toc[DiskInfo.last+1].diskTime.sec;
-       azt_Play.start.frame = Toc[DiskInfo.last+1].diskTime.frame;
-       test=0;
-
-       for (limit=30;limit>0;limit--)   /*Seek for LeadIn of next session*/
-           { if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 1",-1);
-             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 2",-1);
-             if ((qInfo.track==0)&&(qInfo.pointIndex)) break;  /*LeadIn found*/
-             if ((azt_Play.start.sec+=10) > 59)
-                { azt_Play.start.sec=0;
-                  azt_Play.start.min++;
-                }
-           }
-       if (!limit) break;  /*Check, if a leadin track was found, if not we're
-                             at the end of the disk*/
-#ifdef AZT_DEBUG_MULTISESSION
-       printk("leadin found track %d  pointIndex %x  limit %d\n",qInfo.track,qInfo.pointIndex,limit);
-#endif
-       for (limit=300;limit>0;limit--)
-           { if (++azt_Play.start.frame>74)
-                { azt_Play.start.frame=0;
-                  if (azt_Play.start.sec > 59)
-                     { azt_Play.start.sec=0;
-                       azt_Play.start.min++;
-                     }
-                }     
-             if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 3",-1);
-             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 4",-1);
-             if (qInfo.pointIndex==0xA0)   /*Number of NextTrack*/
-	        { DiskInfo.next = qInfo.diskTime.min;
-	          DiskInfo.next = azt_bcd2bin(DiskInfo.next);
-	          test=test|0x01;
-	        }
-             if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
-	 	{ DiskInfo.last  = qInfo.diskTime.min;
-	   	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
-	   	  test=test|0x02;
-	 	}
-      	     if (qInfo.pointIndex==0xA2)   /*DiskLength*/
-	 	{ DiskInfo.diskLength.min  =qInfo.diskTime.min;
-	   	  DiskInfo.diskLength.sec  =qInfo.diskTime.sec;
-	   	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
-	  	  test=test|0x04;
-	 	}
-      	     if ((qInfo.pointIndex==DiskInfo.next)&&(test&0x01))   /*StartTime of Next Track*/
-		{ DiskInfo.nextSession.min=qInfo.diskTime.min;
-	  	  DiskInfo.nextSession.sec=qInfo.diskTime.sec;
-	  	  DiskInfo.nextSession.frame=qInfo.diskTime.frame;
-	  	  test=test|0x08;
-		}
-      	     if (test==0x0F) break;
-   	   }
-#ifdef AZT_DEBUG_MULTISESSION
-       printk ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez  first %02x:%02x.%02x dez  next %02x:%02x.%02x dez\n",
-     		DiskInfo.first,
-		DiskInfo.next,
-		DiskInfo.last,
-		DiskInfo.diskLength.min,
-		DiskInfo.diskLength.sec,
-		DiskInfo.diskLength.frame,
-		DiskInfo.firstTrack.min,
-		DiskInfo.firstTrack.sec,
-		DiskInfo.firstTrack.frame,
-		DiskInfo.nextSession.min,
-		DiskInfo.nextSession.sec,
-		DiskInfo.nextSession.frame);
-#endif
-       if (test!=0x0F) 
-           break;
-       else 
-           DiskInfo.multi=1;   /*found TOC of more than one session*/
-       aztGetToc(1);
-     } while(--k);
-
-#ifdef AZT_DEBUG
-  printk ("aztcd: exiting aztGetMultiDiskInfo  Time:%li\n",jiffies);
-#endif
-  return 0;
-}
-#endif
-
-/*
- * Read the table of contents (TOC)
- */
-static int aztGetToc(int multi)
-{ int i, px;
-  int limit;
-  struct azt_Toc qInfo;
-
-#ifdef AZT_DEBUG
-  printk("aztcd: starting aztGetToc  Time:%li\n",jiffies);
-#endif
-  if (!multi)
-     { for (i = 0; i < MAX_TRACKS; i++)
- 	    Toc[i].pointIndex = 0;
-       i = DiskInfo.last + 3;
-     }
-  else
-     { for (i = DiskInfo.next; i < MAX_TRACKS; i++)
-     	    Toc[i].pointIndex = 0; 
-       i = DiskInfo.last + 4 - DiskInfo.next;
-     }
-
-/*Is there a good reason to stop motor before TOC read?
-  if (aztSendCmd(ACMD_STOP)) RETURNM("aztGetToc 1",-1);
-      STEN_LOW_WAIT;
-*/
-
-  if (!multi)
-     { azt_mode = 0x05;
-       if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1); /*???*/
-       STEN_LOW_WAIT;
-     }
-  for (limit = 300; limit > 0; limit--)
-      { if (multi)
-           { if (++azt_Play.start.sec > 59)
-                { azt_Play.start.sec=0;
-                  azt_Play.start.min++;
-                }
-             if (aztSeek(&azt_Play)) RETURNM("aztGetToc 3",-1);
-           }
-	if (aztGetQChannelInfo(&qInfo) < 0)
-	    break;
-
-	px = azt_bcd2bin(qInfo.pointIndex);
-
-	if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
-  	    if (Toc[px].pointIndex == 0)
-	       { Toc[px] = qInfo;
-		 i--;
-	       }
-
-	if (i <= 0)
-	    break;
-      }
-
-  Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-
-
-#ifdef AZT_DEBUG_MULTISESSION 
-  printk("aztcd: exiting aztGetToc\n");
-  for (i = 1; i <= DiskInfo.last+1; i++)
-       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
-               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-  for (i = 100; i < 103; i++)
-       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
-               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-#endif
-
-  return limit > 0 ? 0 : -1;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{ if (MOD_IN_USE)
-    { printk("aztcd module in use - can't remove it.\n");
-      return;
-    }
-  if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))    
-    { printk("What's that: can't unregister aztcd\n");
-      return;
-    }
-   release_region(azt_port,4);
-   printk("aztcd module released.\n");
-}   
-#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/block/blk.h linux/drivers/block/blk.h
--- v1.3.35/linux/drivers/block/blk.h	Fri Oct 13 14:44:32 1995
+++ linux/drivers/block/blk.h	Thu Jan  1 02:00:00 1970
@@ -1,393 +0,0 @@
-#ifndef _BLK_H
-#define _BLK_H
-
-#include <linux/blkdev.h>
-#include <linux/locks.h>
-#include <linux/config.h>
-
-/*
- * NR_REQUEST is the number of entries in the request-queue.
- * NOTE that writes may use only the low 2/3 of these: reads
- * take precedence.
- */
-#define NR_REQUEST	64
-
-/*
- * This is used in the elevator algorithm: Note that
- * reads always go before writes. This is natural: reads
- * are much more time-critical than writes.
- */
-#define IN_ORDER(s1,s2) \
-((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
-((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
-(s1)->sector < (s2)->sector)))))
-
-/*
- * These will have to be changed to be aware of different buffer
- * sizes etc.. It actually needs a major cleanup.
- */
-#ifdef IDE_DRIVER
-#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
-#else
-#define SECTOR_MASK (blksize_size[MAJOR_NR] &&     \
-	blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] ? \
-	((blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] >> 9) - 1) :  \
-	((BLOCK_SIZE >> 9)  -  1))
-#endif /* IDE_DRIVER */
-
-#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
-
-#ifdef CONFIG_CDU31A
-extern int cdu31a_init(void);
-#endif CONFIG_CDU31A
-#ifdef CONFIG_MCD
-extern int mcd_init(void);
-#endif CONFIG_MCD
-#ifdef CONFIG_MCDX
-extern int mcdx_init(void);
-#endif CONFIG_MCDX
-#ifdef CONFIG_SBPCD
-extern int sbpcd_init(void);
-#endif CONFIG_SBPCD
-#ifdef CONFIG_AZTCD
-extern int aztcd_init(void);
-#endif CONFIG_AZTCD
-#ifdef CONFIG_CDU535
-extern int sony535_init(void);
-#endif CONFIG_CDU535
-#ifdef CONFIG_GSCD
-extern int gscd_init(void);
-#endif CONFIG_GSCD
-#ifdef CONFIG_CM206
-extern int cm206_init(void);
-#endif CONFIG_CM206
-#ifdef CONFIG_OPTCD
-extern int optcd_init(void);
-#endif CONFIG_OPTCD
-#ifdef CONFIG_SJCD
-extern int sjcd_init(void);
-#endif CONFIG_SJCD
-#ifdef CONFIG_BLK_DEV_HD
-extern int hd_init(void);
-#endif
-#ifdef CONFIG_BLK_DEV_IDE
-extern int ide_init(void);
-#endif
-#ifdef CONFIG_BLK_DEV_XD
-extern int xd_init(void);
-#endif
-
-extern void set_device_ro(kdev_t dev,int flag);
-
-extern int floppy_init(void);
-extern void rd_load(void);
-extern long rd_init(long mem_start, int length);
-extern int ramdisk_size;
-
-#define RO_IOCTLS(dev,where) \
-  case BLKROSET: if (!suser()) return -EACCES; \
-		 set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
-  case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
-		   if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
-		 
-#if defined(MAJOR_NR) || defined(IDE_DRIVER)
-
-/*
- * Add entries as needed.
- */
-
-#ifdef IDE_DRIVER
-
-#define DEVICE_NR(device)	(MINOR(device) >> PARTN_BITS)
-#define DEVICE_ON(device)	/* nothing */
-#define DEVICE_OFF(device)	/* nothing */
-
-#elif (MAJOR_NR == MEM_MAJOR)
-
-/* ram disk */
-#define DEVICE_NAME "ramdisk"
-#define DEVICE_REQUEST do_rd_request
-#define DEVICE_NR(device) (MINOR(device) & 7)
-#define DEVICE_ON(device) 
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == FLOPPY_MAJOR)
-
-static void floppy_off(unsigned int nr);
-
-#define DEVICE_NAME "floppy"
-#define DEVICE_INTR do_floppy
-#define DEVICE_REQUEST do_fd_request
-#define DEVICE_NR(device) ( (MINOR(device) & 3) | ((MINOR(device) & 0x80 ) >> 5 ))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
-
-#elif (MAJOR_NR == HD_MAJOR)
-
-/* harddisk: timeout is 6 seconds.. */
-#define DEVICE_NAME "harddisk"
-#define DEVICE_INTR do_hd
-#define DEVICE_TIMEOUT HD_TIMER
-#define TIMEOUT_VALUE (6*HZ)
-#define DEVICE_REQUEST do_hd_request
-#define DEVICE_NR(device) (MINOR(device)>>6)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == SCSI_DISK_MAJOR)
-
-#define DEVICE_NAME "scsidisk"
-#define DEVICE_INTR do_sd  
-#define TIMEOUT_VALUE (2*HZ)
-#define DEVICE_REQUEST do_sd_request
-#define DEVICE_NR(device) (MINOR(device) >> 4)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
-
-#define DEVICE_NAME "scsitape"
-#define DEVICE_INTR do_st  
-#define DEVICE_NR(device) (MINOR(device) & 0x7f)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
-
-#define DEVICE_NAME "CD-ROM"
-#define DEVICE_INTR do_sr
-#define DEVICE_REQUEST do_sr_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == XT_DISK_MAJOR)
-
-#define DEVICE_NAME "xt disk"
-#define DEVICE_REQUEST do_xd_request
-#define DEVICE_NR(device) (MINOR(device) >> 6)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
-
-#define DEVICE_NAME "CDU31A"
-#define DEVICE_REQUEST do_cdu31a_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
-
-#define DEVICE_NAME "Mitsumi CD-ROM"
-/* #define DEVICE_INTR do_mcd */
-#define DEVICE_REQUEST do_mcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
-
-#define DEVICE_NAME "Mitsumi CD-ROM"
-/* #define DEVICE_INTR do_mcdx */
-#define DEVICE_REQUEST do_mcdx_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #1"
-#define DEVICE_REQUEST do_sbpcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #2"
-#define DEVICE_REQUEST do_sbpcd2_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #3"
-#define DEVICE_REQUEST do_sbpcd3_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #4"
-#define DEVICE_REQUEST do_sbpcd4_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
-
-#define DEVICE_NAME "Aztech CD-ROM"
-#define DEVICE_REQUEST do_aztcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
-
-#define DEVICE_NAME "SONY-CDU535"
-#define DEVICE_INTR do_cdu535
-#define DEVICE_REQUEST do_cdu535_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
-
-#define DEVICE_NAME "Goldstar R420"
-#define DEVICE_REQUEST do_gscd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == CM206_CDROM_MAJOR)
-#define DEVICE_NAME "Philips/LMS cd-rom cm206"
-#define DEVICE_REQUEST do_cm206_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
-
-#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
-#define DEVICE_REQUEST do_optcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
-
-#define DEVICE_NAME "Sanyo H94A CD-ROM"
-#define DEVICE_REQUEST do_sjcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#endif /* MAJOR_NR == whatever */
-
-#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
-
-#ifndef CURRENT
-#define CURRENT (blk_dev[MAJOR_NR].current_request)
-#endif
-
-#define CURRENT_DEV DEVICE_NR(CURRENT->rq_dev)
-
-#ifdef DEVICE_INTR
-void (*DEVICE_INTR)(void) = NULL;
-#endif
-#ifdef DEVICE_TIMEOUT
-
-#define SET_TIMER \
-((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
-(timer_active |= 1<<DEVICE_TIMEOUT))
-
-#define CLEAR_TIMER \
-timer_active &= ~(1<<DEVICE_TIMEOUT)
-
-#define SET_INTR(x) \
-if ((DEVICE_INTR = (x)) != NULL) \
-	SET_TIMER; \
-else \
-	CLEAR_TIMER;
-
-#else
-
-#define SET_INTR(x) (DEVICE_INTR = (x))
-
-#endif /* DEVICE_TIMEOUT */
-
-static void (DEVICE_REQUEST)(void);
-
-#ifdef DEVICE_INTR
-#define CLEAR_INTR SET_INTR(NULL)
-#else
-#define CLEAR_INTR
-#endif
-
-#define INIT_REQUEST \
-	if (!CURRENT) {\
-		CLEAR_INTR; \
-		return; \
-	} \
-	if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
-		panic(DEVICE_NAME ": request list destroyed"); \
-	if (CURRENT->bh) { \
-		if (!CURRENT->bh->b_lock) \
-			panic(DEVICE_NAME ": block not locked"); \
-	}
-
-#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) */
-
-/* end_request() - SCSI devices have their own version */
-/*               - IDE drivers have their own copy too */
-
-#if ! SCSI_MAJOR(MAJOR_NR)
-
-#if defined(_IDE_CD_C) || defined(_TRITON_C) /* shares copy with ide.c */
-void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
-#else
-
-#ifdef IDE_DRIVER
-void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) {
-	struct request *req = hwgroup->rq;
-#else
-static void end_request(int uptodate) {
-	struct request *req = CURRENT;
-#endif /* IDE_DRIVER */
-	struct buffer_head * bh;
-
-	req->errors = 0;
-	if (!uptodate) {
-		printk("end_request: I/O error, dev %s, sector %lu\n",
-			kdevname(req->rq_dev), req->sector);
-		req->nr_sectors--;
-		req->nr_sectors &= ~SECTOR_MASK;
-		req->sector += (BLOCK_SIZE / 512);
-		req->sector &= ~SECTOR_MASK;		
-	}
-
-	if ((bh = req->bh) != NULL) {
-		req->bh = bh->b_reqnext;
-		bh->b_reqnext = NULL;
-		bh->b_uptodate = uptodate;		
-		unlock_buffer(bh);
-		if ((bh = req->bh) != NULL) {
-			req->current_nr_sectors = bh->b_size >> 9;
-			if (req->nr_sectors < req->current_nr_sectors) {
-				req->nr_sectors = req->current_nr_sectors;
-				printk("end_request: buffer-list destroyed\n");
-			}
-			req->buffer = bh->b_data;
-			return;
-		}
-	}
-#ifdef IDE_DRIVER
-	hwgroup->rq = NULL;
-#else
-	DEVICE_OFF(req->rq_dev);
-	CURRENT = req->next;
-#endif /* IDE_DRIVER */
-	if (req->sem != NULL)
-		up(req->sem);
-	req->rq_status = RQ_INACTIVE;
-	wake_up(&wait_for_request);
-}
-#endif /* ndef _IDE_CD_C */
-#endif /* ! SCSI_MAJOR(MAJOR_NR) */
-
-#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
-
-#endif /* _BLK_H */
diff -u --recursive --new-file v1.3.35/linux/drivers/block/cdu31a.c linux/drivers/block/cdu31a.c
--- v1.3.35/linux/drivers/block/cdu31a.c	Tue Oct 10 18:46:31 1995
+++ linux/drivers/block/cdu31a.c	Thu Jan  1 02:00:00 1970
@@ -1,3104 +0,0 @@
-/*
- * Sony CDU-31A CDROM interface device driver.
- *
- * Corey Minyard (minyard@wf-rch.cirr.com)
- *
- * Colossians 3:17
- *
- * The Sony interface device driver handles Sony interface CDROM
- * drives and provides a complete block-level interface as well as an
- * ioctl() interface compatible with the Sun (as specified in
- * include/linux/cdrom.h).  With this interface, CDROMs can be
- * accessed and standard audio CDs can be played back normally.
- *
- * WARNING - 	All autoprobes have been removed from the driver.
- *		You MUST configure the CDU31A via a LILO config
- *		at boot time or in lilo.conf.  I have the
- *		following in my lilo.conf:
- *
- *                append="cdu31a=0x1f88,0,PAS"
- *
- *		The first number is the I/O base address of the
- *		card.  The second is the interrupt (0 means none).
- *		The third should be "PAS" if on a Pro-Audio
- *		spectrum, or nothing if on something else.
- *
- * This interface is (unfortunately) a polled interface.  This is
- * because most Sony interfaces are set up with DMA and interrupts
- * disables.  Some (like mine) do not even have the capability to
- * handle interrupts or DMA.  For this reason you will see a lot of
- * the following:
- *
- *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
- *   while ((retry_count > jiffies) && (! <some condition to wait for))
- *   {
- *      while (handle_sony_cd_attention())
- *         ;
- *
- *      sony_sleep();
- *   }
- *   if (the condition not met)
- *   {
- *      return an error;
- *   }
- *
- * This ugly hack waits for something to happen, sleeping a little
- * between every try.  it also handles attentions, which are
- * asynchronous events from the drive informing the driver that a disk
- * has been inserted, removed, etc.
- *
- * NEWS FLASH - The driver now supports interrupts but they are
- * turned off by default.  Use of interrupts is highly encouraged, it
- * cuts CPU usage down to a reasonable level.  I had DMA in for a while
- * but PC DMA is just too slow.  Better to just insb() it.
- *
- * One thing about these drives: They talk in MSF (Minute Second Frame) format.
- * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
- * disk.  The funny thing is that these are sent to the drive in BCD, but the
- * interface wants to see them in decimal.  A lot of conversion goes on.
- *
- * DRIVER SPECIAL FEATURES
- * -----------------------
- *
- * This section describes features beyond the normal audio and CD-ROM
- * functions of the drive.
- *
- * 2048 byte buffer mode
- *
- * If a disk is mounted with -o block=2048, data is copied straight
- * from the drive data port to the buffer.  Otherwise, the readahead
- * buffer must be involved to hold the other 1K of data when a 1K
- * block operation is done.  Note that with 2048 byte blocks you
- * cannot execute files from the CD.
- *
- * XA compatibility
- *
- * The driver should support XA disks for both the CDU31A and CDU33A.
- * It does this transparently, the using program doesn't need to set it.
- *
- * Multi-Session
- *
- * A multi-session disk looks just like a normal disk to the user.
- * Just mount one normally, and all the data should be there.
- * A special thanks to Koen for help with this!
- * 
- * Raw sector I/O
- *
- * Using the CDROMREADAUDIO it is possible to read raw audio and data
- * tracks.  Both operations return 2352 bytes per sector.  On the data
- * tracks, the first 12 bytes is not returned by the drive and the value
- * of that data is indeterminate.
- *
- *
- *  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.
- *
- */
-
-/*
- *
- * Setting up the Sony CDU31A/CDU33A drive interface card.  If
- * You have another card, you are on your own.
- * 
- *      +----------+-----------------+----------------------+
- *      |  JP1     |  34 Pin Conn    |                      |
- *      |  JP2     +-----------------+                      |
- *      |  JP3                                              |
- *      |  JP4                                              |
- *      |                                                   +--+
- *      |                                                   |  +-+
- *      |                                                   |  | |  External
- *      |                                                   |  | |  Connector
- *      |                                                   |  | |
- *      |                                                   |  +-+
- *      |                                                   +--+
- *      |                                                   |
- *      |                                          +--------+
- *      |                                          |
- *      +------------------------------------------+
- * 
- *    JP1 sets the Base Address, using the following settings:
- * 
- *      Address         Pin 1           Pin 2
- *      -------         -----           -----
- *      0x320           Short           Short
- *      0x330           Short           Open
- *      0x340           Open            Short
- *      0x360           Open            Open
- * 
- *    JP2 and JP3 configure the DMA channel; they must be set the same.
- * 
- *      DMA             Pin 1           Pin 2           Pin 3
- *      ---             -----           -----           -----
- *      1               On              Off             On
- *      2               Off             On              Off
- *      3               Off             Off             On
- * 
- *    JP4 Configures the IRQ:
- * 
- *      IRQ     Pin 1           Pin 2           Pin 3           Pin 4
- *      ---     -----           -----           -----           -----
- *      3       Off             Off             On              Off
- *      4       Off             Off*            Off             On
- *      5       On              Off             Off             Off
- *      6       Off             On              Off             Off
- * 
- *              * The documentation states to set this for interrupt
- *                4, but I think that is a mistake.
- */
-
-#include <linux/major.h>
-#include <linux/config.h>
-
-#ifdef MODULE
-# include <linux/module.h>
-# include <linux/version.h>
-# ifndef CONFIG_MODVERSIONS
-    char kernel_version[]= UTS_RELEASE;
-# endif
-#endif
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/hdreg.h>
-#include <linux/genhd.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-#include <asm/dma.h>
-
-#include <linux/cdrom.h>
-#include <linux/cdu31a.h>
-
-#define MAJOR_NR CDU31A_CDROM_MAJOR
-#include "blk.h"
-
-#define DEBUG 0
-
-#define CDU31A_READAHEAD 128  /* 128 sector, 64kB, 32 reads read-ahead */
-#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
-
-/* Define the following if you have data corruption problems. */
-#undef SONY_POLL_EACH_BYTE
-
-/*
-** Edit the following data to change interrupts, DMA channels, etc.
-** Default is polled and no DMA.  DMA is not recommended for double-speed
-** drives.
-*/
-static struct
-{
-   unsigned short base;         /* I/O Base Address */
-   short          int_num;      /* Interrupt Number (-1 means scan for it,
-                                   0 means don't use) */
-} cdu31a_addresses[] =
-{
-#if 0	/* No autoconfig any more. See Note at beginning
-	   of this file. */
-   { 0x340,     0 },    /* Standard configuration Sony Interface */
-   { 0x1f88,    0 },    /* Fusion CD-16 */
-   { 0x230,     0 },    /* SoundBlaster 16 card */
-   { 0x360,     0 },    /* Secondary standard Sony Interface */
-   { 0x320,     0 },    /* Secondary standard Sony Interface */
-   { 0x330,     0 },    /* Secondary standard Sony Interface */
-   { 0x634,     0 },    /* Sound FX SC400 */
-   { 0x654,     0 },    /* Sound FX SC400 */
-#endif
-   { 0 }
-};
-
-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 void do_sony_cd_cmd(unsigned char cmd,
-                           unsigned char *params,
-                           unsigned int num_params,
-                           unsigned char *result_buffer,
-                           unsigned int *result_size);
-static void size_to_buf(unsigned int size,
-                        unsigned char *buf);
-
-/* Parameters for the read-ahead. */
-static unsigned int sony_next_block;      /* Next 512 byte block offset */
-static unsigned int sony_blocks_left = 0; /* Number of 512 byte blocks left
-                                             in the current read command. */
-
-
-/* The base I/O address of the Sony Interface.  This is a variable (not a
-   #define) so it can be easily changed via some future ioctl() */
-static unsigned short cdu31a_port = 0;
-
-/*
- * The following are I/O addresses of the various registers for the drive.  The
- * comment for the base address also applies here.
- */
-static volatile unsigned short sony_cd_cmd_reg;
-static volatile unsigned short sony_cd_param_reg;
-static volatile unsigned short sony_cd_write_reg;
-static volatile unsigned short sony_cd_control_reg;
-static volatile unsigned short sony_cd_status_reg;
-static volatile unsigned short sony_cd_result_reg;
-static volatile unsigned short sony_cd_read_reg;
-static volatile unsigned short sony_cd_fifost_reg;
-
-
-static int sony_spun_up = 0;               /* Has the drive been spun up? */
-
-static int sony_xa_mode = 0;               /* Is an XA disk in the drive
-					      and the drive a CDU31A? */
-
-static int sony_raw_data_mode = 1;         /* 1 if data tracks, 0 if audio.
-                                              For raw data reads. */
-
-static unsigned int sony_usage = 0;        /* How many processes have the
-                                              drive open. */
-
-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 int sony_toc_read = 0;		   /* Has the TOC been read for
-					      the drive? */
-
-static struct s_sony_subcode last_sony_subcode; /* Points to the last
-                                                   subcode address read */
-
-static volatile int sony_inuse = 0;  /* Is the drive in use?  Only one operation
-					at a time allowed */
-
-static struct wait_queue * sony_wait = NULL;	/* Things waiting for the drive */
-
-static struct task_struct *has_cd_task = NULL;  /* The task that is currently
-						   using the CDROM drive, or
-						   NULL if none. */
-
-static int is_double_speed = 0; /* Is the drive a CDU33A? */
-
-/*
- * The audio status uses the values from read subchannel data as specified
- * in include/linux/cdrom.h.
- */
-static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;
-
-/*
- * The following are a hack for pausing and resuming audio play.  The drive
- * does not work as I would expect it, if you stop it then start it again,
- * the drive seeks back to the beginning and starts over.  This holds the
- * position during a pause so a resume can restart it.  It uses the
- * audio status variable above to tell if it is paused.
- */
-static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
-static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
-
-/* What IRQ is the drive using?  0 if none. */
-static int cdu31a_irq = 0;
-
-/* The interrupt handler will wake this queue up when it gets an
-   interrupts. */
-static struct wait_queue *cdu31a_irq_wait = NULL;
-
-static int curr_control_reg = 0; /* Current value of the control register */
-
-/* A disk changed variable.  When a disk change is detected, it will
-   all be set to TRUE.  As the upper layers ask for disk_changed status
-   it will be cleared. */
-static char disk_changed;
-
-/* Variable for using the readahead buffer.  The readahead buffer
-   is used for raw sector reads and for blocksizes that are smaller
-   than 2048 bytes. */
-static char readahead_buffer[CD_FRAMESIZE_RAW];
-static int readahead_dataleft = 0;
-static int readahead_bad = 0;
-
-/* Used to time a short period to abort an operation after the
-   drive has been idle for a while.  This keeps the light on
-   the drive from flashing for very long. */
-static struct timer_list cdu31a_abort_timer;
-
-/* Marks if the timeout has started an abort read.  This is used
-   on entry to the drive to tell the code to read out the status
-   from the abort read. */
-static int abort_read_started = 0;
-
-
-/*
- * This routine returns 1 if the disk has been changed since the last
- * check or 0 if it hasn't.
- */
-static int
-scd_disk_change(kdev_t full_dev)
-{
-   int retval;
-
-   retval = disk_changed;
-   disk_changed = 0;
-
-   return retval;
-}
-
-static inline void
-enable_interrupts(void)
-{
-   curr_control_reg |= (  SONY_ATTN_INT_EN_BIT
-                        | SONY_RES_RDY_INT_EN_BIT
-                        | SONY_DATA_RDY_INT_EN_BIT);
-   outb(curr_control_reg, sony_cd_control_reg);
-}
-
-static inline void
-disable_interrupts(void)
-{
-   curr_control_reg &= ~(  SONY_ATTN_INT_EN_BIT
-                         | SONY_RES_RDY_INT_EN_BIT
-                         | SONY_DATA_RDY_INT_EN_BIT);
-   outb(curr_control_reg, sony_cd_control_reg);
-}
-
-/*
- * Wait a little while (used for polling the drive).  If in initialization,
- * setting a timeout doesn't work, so just loop for a while.
- */
-static inline void
-sony_sleep(void)
-{
-   unsigned long flags;
-
-   if (cdu31a_irq <= 0)
-   {
-      current->state = TASK_INTERRUPTIBLE;
-      current->timeout = jiffies;
-      schedule();
-   }
-   else /* Interrupt driven */
-   {
-      save_flags(flags);
-      cli();
-      enable_interrupts();
-      interruptible_sleep_on(&cdu31a_irq_wait);
-      restore_flags(flags);
-   }
-}
-
-
-/*
- * The following are convenience routine to read various status and set
- * various conditions in the drive.
- */
-static inline int
-is_attention(void)
-{
-   return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);
-}
-
-static inline int
-is_busy(void)
-{
-   return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);
-}
-
-static inline int
-is_data_ready(void)
-{
-   return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);
-}
-
-static inline int
-is_data_requested(void)
-{
-   return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);
-}
-
-static inline int
-is_result_ready(void)
-{
-   return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);
-}
-
-static inline int
-is_param_write_rdy(void)
-{
-   return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
-}
-
-static inline int
-is_result_reg_not_empty(void)
-{
-   return((inb(sony_cd_fifost_reg) & SONY_RES_REG_NOT_EMP_BIT) != 0);
-}
-
-static inline void
-reset_drive(void)
-{
-   curr_control_reg = 0;
-   outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_attention(void)
-{
-   outb(curr_control_reg | SONY_ATTN_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_result_ready(void)
-{
-   outb(curr_control_reg | SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_data_ready(void)
-{
-   outb(curr_control_reg | SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_param_reg(void)
-{
-   outb(curr_control_reg | SONY_PARAM_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline unsigned char
-read_status_register(void)
-{
-   return(inb(sony_cd_status_reg));
-}
-
-static inline unsigned char
-read_result_register(void)
-{
-   return(inb(sony_cd_result_reg));
-}
-
-static inline unsigned char
-read_data_register(void)
-{
-   return(inb(sony_cd_read_reg));
-}
-
-static inline void
-write_param(unsigned char param)
-{
-   outb(param, sony_cd_param_reg);
-}
-
-static inline void
-write_cmd(unsigned char cmd)
-{
-   outb(curr_control_reg | SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
-   outb(cmd, sony_cd_cmd_reg);
-}
-
-static void
-cdu31a_interrupt(int irq, struct pt_regs *regs)
-{
-   unsigned char val;
-
-   if (abort_read_started)
-   {
-      /* We might be waiting for an abort to finish.  Don't
-         disable interrupts yet, though, because we handle
-         this one here. */
-      /* Clear out the result registers. */
-      while (is_result_reg_not_empty())
-      {
-         val = read_result_register();
-      }
-      clear_data_ready();
-      clear_result_ready();
-
-      /* Clear out the data */
-      while (is_data_requested())
-      {
-         val = read_data_register();
-      }
-      abort_read_started = 0;
-
-      /* If something was waiting, wake it up now. */
-      if (cdu31a_irq_wait != NULL)
-      {
-         disable_interrupts();
-         wake_up(&cdu31a_irq_wait);
-      }
-   }
-   else if (cdu31a_irq_wait != NULL)
-   {
-      disable_interrupts();
-      wake_up(&cdu31a_irq_wait);
-   }
-   else
-   {
-      disable_interrupts();
-      printk("CDU31A: Got an interrupt but nothing was waiting\n");
-   }
-}
-
-/*
- * Set the drive parameters so the drive will auto-spin-up when a
- * disk is inserted.
- */
-static void
-set_drive_params(void)
-{
-   unsigned char res_reg[12];
-   unsigned int res_size;
-   unsigned char params[3];
-
-
-   params[0] = SONY_SD_AUTO_SPIN_DOWN_TIME;
-   params[1] = 0x00; /* Never spin down the drive. */
-   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
-                  params,
-                  2,
-                  res_reg,
-                  &res_size);
-   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-   {
-      printk("  Unable to set spin-down time: 0x%2.2x\n", res_reg[1]);
-   }
-
-   params[0] = SONY_SD_MECH_CONTROL;
-   params[1] = 0x03; /* Set auto spin up and auto eject */
-   if (is_double_speed)
-   {
-      params[1] |= 0x04; /* Set the drive to double speed if possible */
-   }
-   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
-                  params,
-                  2,
-                  res_reg,
-                  &res_size);
-   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-   {
-      printk("  Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
-   }
-}
-
-/*
- * This code will reset the drive and attempt to restore sane parameters.
- */
-static void
-restart_on_error(void)
-{
-   unsigned char res_reg[12];
-   unsigned int res_size;
-   unsigned int retry_count;
-
-
-   printk("cdu31a: Resetting drive on error\n");
-   reset_drive();
-   retry_count = jiffies + SONY_RESET_TIMEOUT;
-   while ((retry_count > jiffies) && (!is_attention()))
-   {
-      sony_sleep();
-   }
-   set_drive_params();
-   do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-   {
-      printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]);
-   }
-
-   current->state = TASK_INTERRUPTIBLE;
-   current->timeout = jiffies + 2*HZ;
-   schedule();
-
-   do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
-   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-   {
-      printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]);
-   }
-}
-
-/*
- * This routine writes data to the parameter register.  Since this should
- * happen fairly fast, it is polled with no OS waits between.
- */
-static int
-write_params(unsigned char *params,
-             int num_params)
-{
-   unsigned int retry_count;
-
-
-   retry_count = SONY_READY_RETRIES;
-   while ((retry_count > 0) && (!is_param_write_rdy()))
-   {
-      retry_count--;
-   }
-   if (!is_param_write_rdy())
-   {
-      return -EIO;
-   }
-
-   while (num_params > 0)
-   {
-      write_param(*params);
-      params++;
-      num_params--;
-   }
-
-   return 0;
-}
-
-
-/*
- * The following reads data from the command result register.  It is a
- * fairly complex routine, all status info flows back through this
- * interface.  The algorithm is stolen directly from the flowcharts in
- * the drive manual.
- */
-static void
-get_result(unsigned char *result_buffer,
-           unsigned int *result_size)
-{
-   unsigned char a, b;
-   int i;
-   unsigned int retry_count;
-
-
-   while (handle_sony_cd_attention())
-      ;
-   /* Wait for the result data to be ready */
-   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-   while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
-   {
-      sony_sleep();
-
-      while (handle_sony_cd_attention())
-         ;
-   }
-   if (is_busy() || (!(is_result_ready())))
-   {
-#if DEBUG
-      printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-      result_buffer[0] = 0x20;
-      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
-      *result_size = 2;
-      return;
-   }
-
-   /*
-    * Get the first two bytes.  This determines what else needs
-    * to be done.
-    */
-   clear_result_ready();
-   a = read_result_register();
-   *result_buffer = a;
-   result_buffer++;
-
-   /* Check for block error status result. */
-   if ((a & 0xf0) == 0x50)
-   {
-      *result_size = 1;
-      return;
-   }
-
-   b = read_result_register();
-   *result_buffer = b;
-   result_buffer++;
-   *result_size = 2;
-
-   /*
-    * 0x20 means an error occurred.  Byte 2 will have the error code.
-    * Otherwise, the command succeeded, byte 2 will have the count of
-    * how many more status bytes are coming.
-    *
-    * The result register can be read 10 bytes at a time, a wait for
-    * result ready to be asserted must be done between every 10 bytes.
-    */
-   if ((a & 0xf0) != 0x20)
-   {
-      if (b > 8)
-      {
-         for (i=0; i<8; i++)
-         {
-            *result_buffer = read_result_register();
-            result_buffer++;
-            (*result_size)++;
-         }
-         b = b - 8;
-
-         while (b > 10)
-         {
-            retry_count = SONY_READY_RETRIES;
-            while ((retry_count > 0) && (!is_result_ready()))
-            {
-               retry_count--;
-            }
-            if (!is_result_ready())
-            {
-#if DEBUG
-               printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-               result_buffer[0] = 0x20;
-               result_buffer[1] = SONY_TIMEOUT_OP_ERR;
-               *result_size = 2;
-               return;
-            }
-
-            clear_result_ready();
-                                
-            for (i=0; i<10; i++)
-            {
-               *result_buffer = read_result_register();
-               result_buffer++;
-               (*result_size)++;
-            }
-            b = b - 10;
-         }
-
-         if (b > 0)
-         {
-            retry_count = SONY_READY_RETRIES;
-            while ((retry_count > 0) && (!is_result_ready()))
-            {
-               retry_count--;
-            }
-            if (!is_result_ready())
-            {
-#if DEBUG
-               printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-               result_buffer[0] = 0x20;
-               result_buffer[1] = SONY_TIMEOUT_OP_ERR;
-               *result_size = 2;
-               return;
-            }
-         }
-      }
-
-      while (b > 0)
-      {
-         *result_buffer = read_result_register();
-         result_buffer++;
-         (*result_size)++;
-         b--;
-      }
-   }
-}
-
-/*
- * Do a command that does not involve data transfer.  This routine must
- * be re-entrant from the same task to support being called from the
- * data operation code when an error occurs.
- */
-static void
-do_sony_cd_cmd(unsigned char cmd,
-               unsigned char *params,
-               unsigned int num_params,
-               unsigned char *result_buffer,
-               unsigned int *result_size)
-{
-   unsigned int retry_count;
-   int num_retries;
-   int recursive_call;
-   unsigned long flags;
-
-
-   save_flags(flags);
-   cli();
-   if (current != has_cd_task) /* Allow recursive calls to this routine */
-   {
-      while (sony_inuse)
-      {
-         interruptible_sleep_on(&sony_wait);
-         if (current->signal & ~current->blocked)
-         {
-            result_buffer[0] = 0x20;
-            result_buffer[1] = SONY_SIGNAL_OP_ERR;
-            *result_size = 2;
-            restore_flags(flags);
-            return;
-         }
-      }
-      sony_inuse = 1;
-      has_cd_task = current;
-      recursive_call = 0;
-   }
-   else
-   {
-      recursive_call = 1;
-   }
-
-   num_retries = 0;
-retry_cd_operation:
-
-   while (handle_sony_cd_attention())
-      ;
-
-   sti();
-   
-   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-   while ((retry_count > jiffies) && (is_busy()))
-   {
-      sony_sleep();
-      
-      while (handle_sony_cd_attention())
-         ;
-   }
-   if (is_busy())
-   {
-#if DEBUG
-      printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-      result_buffer[0] = 0x20;
-      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
-      *result_size = 2;
-   }
-   else
-   {
-      clear_result_ready();
-      clear_param_reg();
-
-      write_params(params, num_params);
-      write_cmd(cmd);
-
-      get_result(result_buffer, result_size);
-   }
-
-   if (   ((result_buffer[0] & 0xf0) == 0x20)
-       && (num_retries < MAX_CDU31A_RETRIES))
-   {
-      num_retries++;
-      current->state = TASK_INTERRUPTIBLE;
-      current->timeout = jiffies + HZ/10; /* Wait .1 seconds on retries */
-      schedule();
-      goto retry_cd_operation;
-   }
-
-   if (!recursive_call)
-   {
-      has_cd_task = NULL;
-      sony_inuse = 0;
-      wake_up_interruptible(&sony_wait);
-   }
-
-   restore_flags(flags);
-}
-
-
-/*
- * Handle an attention from the drive.  This will return 1 if it found one
- * or 0 if not (if one is found, the caller might want to call again).
- *
- * This routine counts the number of consecutive times it is called
- * (since this is always called from a while loop until it returns
- * a 0), and returns a 0 if it happens too many times.  This will help
- * prevent a lockup.
- */
-static int
-handle_sony_cd_attention(void)
-{
-   unsigned char atten_code;
-   static int num_consecutive_attentions = 0;
-   volatile int val;
-
-
-   if (abort_read_started)
-   {
-      while (is_result_reg_not_empty())
-      {
-         val = read_result_register();
-      }
-      clear_data_ready();
-      clear_result_ready();
-      /* Clear out the data */
-      while (is_data_requested())
-      {
-         val = read_data_register();
-      }
-      abort_read_started = 0;
-      return(1);
-   }
-   else if (is_attention())
-   {
-      if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS)
-      {
-         printk("cdu31a: Too many consecutive attentions: %d\n",
-                num_consecutive_attentions);
-         num_consecutive_attentions = 0;
-         return(0);
-      }
-
-      clear_attention();
-      atten_code = read_result_register();
-
-      switch (atten_code)
-      {
-       /* Someone changed the CD.  Mark it as changed */
-      case SONY_MECH_LOADED_ATTN:
-         disk_changed = 1;
-	 sony_toc_read = 0;
-         sony_audio_status = CDROM_AUDIO_NO_STATUS;
-         sony_blocks_left = 0;
-         break;
-
-      case SONY_SPIN_DOWN_COMPLETE_ATTN:
-         /* Mark the disk as spun down. */
-         sony_spun_up = 0;
-         break;
-
-      case SONY_AUDIO_PLAY_DONE_ATTN:
-         sony_audio_status = CDROM_AUDIO_COMPLETED;
-         read_subcode();
-         break;
-
-      case SONY_EJECT_PUSHED_ATTN:
-         sony_audio_status = CDROM_AUDIO_INVALID;
-         break;
-
-      case SONY_LEAD_IN_ERR_ATTN:
-      case SONY_LEAD_OUT_ERR_ATTN:
-      case SONY_DATA_TRACK_ERR_ATTN:
-      case SONY_AUDIO_PLAYBACK_ERR_ATTN:
-         sony_audio_status = CDROM_AUDIO_ERROR;
-         break;
-      }
-
-      num_consecutive_attentions++;
-      return(1);
-   }
-
-   num_consecutive_attentions = 0;
-   return(0);
-}
-
-
-/* Convert from an integer 0-99 to BCD */
-static inline unsigned int
-int_to_bcd(unsigned int val)
-{
-   int retval;
-
-
-   retval = (val / 10) << 4;
-   retval = retval | val % 10;
-   return(retval);
-}
-
-
-/* Convert from BCD to an integer from 0-99 */
-static unsigned int
-bcd_to_int(unsigned int bcd)
-{
-   return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
-}
-
-
-/*
- * Convert a logical sector value (like the OS would want to use for
- * a block device) to an MSF format.
- */
-static void
-log_to_msf(unsigned int log, unsigned char *msf)
-{
-   log = log + LOG_START_OFFSET;
-   msf[0] = int_to_bcd(log / 4500);
-   log = log % 4500;
-   msf[1] = int_to_bcd(log / 75);
-   msf[2] = int_to_bcd(log % 75);
-}
-
-
-/*
- * Convert an MSF format to a logical sector.
- */
-static unsigned int
-msf_to_log(unsigned char *msf)
-{
-   unsigned int log;
-
-
-   log = bcd_to_int(msf[2]);
-   log += bcd_to_int(msf[1]) * 75;
-   log += bcd_to_int(msf[0]) * 4500;
-   log = log - LOG_START_OFFSET;
-
-   return log;
-}
-
-
-/*
- * Take in integer size value and put it into a buffer like
- * the drive would want to see a number-of-sector value.
- */
-static void
-size_to_buf(unsigned int size,
-            unsigned char *buf)
-{
-   buf[0] = size / 65536;
-   size = size % 65536;
-   buf[1] = size / 256;
-   buf[2] = size % 256;
-}
-
-/* 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
-   operation if the requested sector is not the next one from the
-   drive. */
-static int
-start_request(unsigned int sector,
-              unsigned int nsect,
-              int          read_nsect_only)
-{
-   unsigned char params[6];
-   unsigned int read_size;
-   unsigned int retry_count;
-
-
-   log_to_msf(sector, params);
-   /* If requested, read exactly what was asked. */
-   if (read_nsect_only)
-   {
-      read_size = nsect;
-   }
-   /*
-    * If the full read-ahead would go beyond the end of the media, trim
-    * it back to read just till the end of the media.
-    */
-   else if ((sector + nsect) >= sony_toc.lead_out_start_lba)
-   {
-      read_size = sony_toc.lead_out_start_lba - sector;
-   }
-   /* Read the full readahead amount. */
-   else
-   {
-      read_size = CDU31A_READAHEAD;
-   }
-   size_to_buf(read_size, &params[3]);
-
-   /*
-    * Clear any outstanding attentions and wait for the drive to
-    * complete any pending operations.
-    */
-   while (handle_sony_cd_attention())
-      ;
-
-   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-   while ((retry_count > jiffies) && (is_busy()))
-   {
-      sony_sleep();
-      
-      while (handle_sony_cd_attention())
-         ;
-   }
-
-   if (is_busy())
-   {
-      printk("CDU31A: Timeout while waiting to issue command\n");
-      return(1);
-   }
-   else
-   {
-      /* Issue the command */
-      clear_result_ready();
-      clear_param_reg();
-
-      write_params(params, 6);
-      write_cmd(SONY_READ_BLKERR_STAT_CMD);
-
-      sony_blocks_left = read_size * 4;
-      sony_next_block = sector * 4;
-      readahead_dataleft = 0;
-      readahead_bad = 0;
-      return(0);
-   }
-}
-
-/* Abort a pending read operation.  Clear all the drive status and
-   readahead variables. */
-static void
-abort_read(void)
-{
-   unsigned char result_reg[2];
-   int           result_size;
-   volatile int  val;
-
-
-   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]);
-   }
-
-   while (is_result_reg_not_empty())
-   {
-      val = read_result_register();
-   }
-   clear_data_ready();
-   clear_result_ready();
-   /* Clear out the data */
-   while (is_data_requested())
-   {
-      val = read_data_register();
-   }
-
-   sony_blocks_left = 0;
-   readahead_dataleft = 0;
-   readahead_bad = 0;
-}
-
-/* Called when the timer times out.  This will abort the
-   pending read operation. */
-static void
-handle_abort_timeout(unsigned long data)
-{
-   /* If it is in use, ignore it. */
-   if (!sony_inuse)
-   {
-      /* We can't use abort_read(), because it will sleep
-         or schedule in the timer interrupt.  Just start
-         the operation, finish it on the next access to
-         the drive. */
-      clear_result_ready();
-      clear_param_reg();
-      write_cmd(SONY_ABORT_CMD);
-
-      sony_blocks_left = 0;
-      readahead_dataleft = 0;
-      readahead_bad = 0;
-      abort_read_started = 1;
-   }
-}
-
-/* Actually get data and status from the drive. */
-static void
-input_data(char         *buffer,
-           unsigned int bytesleft,
-           unsigned int nblocks,
-           unsigned int offset,
-           unsigned int skip)
-{
-   int i;
-   volatile unsigned char val;
-
-
-   /* If an XA disk on a CDU31A, skip the first 12 bytes of data from
-      the disk.  The real data is after that. */
-   if (sony_xa_mode)
-   {
-      for(i=0; i<CD_XA_HEAD; i++)
-      {
-         val = read_data_register();
-      }
-   }
-
-   clear_data_ready();
-
-   if (bytesleft == 2048) /* 2048 byte direct buffer transfer */
-   {
-      insb(sony_cd_read_reg, buffer, 2048);
-      readahead_dataleft = 0;
-   }
-   else
-   {
-      /* If the input read did not align with the beginning of the block,
-	 skip the necessary bytes. */
-      if (skip != 0)
-      {
-         insb(sony_cd_read_reg, readahead_buffer, skip);
-      }
-
-      /* Get the data into the buffer. */
-      insb(sony_cd_read_reg, &buffer[offset], bytesleft);
-
-      /* Get the rest of the data into the readahead buffer at the
-	 proper location. */
-      readahead_dataleft = (2048 - skip) - bytesleft;
-      insb(sony_cd_read_reg,
-           readahead_buffer + bytesleft,
-           readahead_dataleft);
-   }
-   sony_blocks_left -= nblocks;
-   sony_next_block += nblocks; 
-
-   /* If an XA disk, we have to clear out the rest of the unused
-      error correction data. */
-   if (sony_xa_mode)
-   {
-      for(i=0; i<CD_XA_TAIL; i++)
-      {
-         val = read_data_register();
-      }
-   }
-}
-
-/* read data from the drive.  Note the nsect must be <= 4. */
-static void
-read_data_block(char          *buffer,
-                unsigned int  block,
-                unsigned int  nblocks,
-                unsigned char res_reg[],
-                int           *res_size)
-{
-   unsigned int retry_count;
-   unsigned int bytesleft;
-   unsigned int offset;
-   unsigned int skip;
-
-
-   res_reg[0] = 0;
-   res_reg[1] = 0;
-   *res_size = 0;
-   bytesleft = nblocks * 512;
-   offset = 0;
-
-   /* If the data in the read-ahead does not match the block offset,
-      then fix things up. */
-   if (((block % 4) * 512) != ((2048 - readahead_dataleft) % 2048))
-   {
-      sony_next_block += block % 4;
-      sony_blocks_left -= block % 4;
-      skip = (block % 4) * 512;
-   }
-   else
-   {
-      skip = 0;
-   }
-
-   /* We have readahead data in the buffer, get that first before we
-      decide if a read is necessary. */
-   if (readahead_dataleft != 0)
-   {
-      if (bytesleft > readahead_dataleft)
-      {
-	 /* The readahead will not fill the requested buffer, but
-	    get the data out of the readahead into the buffer. */
-         memcpy(buffer,
-                readahead_buffer + (2048 - readahead_dataleft),
-                readahead_dataleft);
-         readahead_dataleft = 0;
-         bytesleft -= readahead_dataleft;
-         offset += readahead_dataleft;
-      }
-      else
-      {
-	 /* The readahead will fill the whole buffer, get the data
-	    and return. */
-         memcpy(buffer,
-                readahead_buffer + (2048 - readahead_dataleft),
-                bytesleft);
-         readahead_dataleft -= bytesleft;
-         bytesleft = 0;
-         sony_blocks_left -= nblocks;
-         sony_next_block += nblocks;
-
-	 /* If the data in the readahead is bad, return an error so the
-	    driver will abort the buffer. */
-         if (readahead_bad)
-         {
-            res_reg[0] = 0x20;
-            res_reg[1] = SONY_BAD_DATA_ERR;
-            *res_size = 2;
-         }
-
-         if (readahead_dataleft == 0)
-         {
-            readahead_bad = 0;
-         }
-
-         /* Final transfer is done for read command, get final result. */
-         if (sony_blocks_left == 0)
-         {
-            get_result(res_reg, res_size);
-         }
-         return;
-      }
-   }
-
-   /* Wait for the drive to tell us we have something */
-   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-   while ((retry_count > jiffies) && !(is_data_ready()))
-   {
-      while (handle_sony_cd_attention())
-         ;
-
-      sony_sleep();
-   }
-   if (!(is_data_ready()))
-   {
-      if (is_result_ready())
-      {
-         get_result(res_reg, res_size);
-         if ((res_reg[0] & 0xf0) != 0x20)
-         {
-            printk("CDU31A: Got result that should have been error: %d\n",
-                   res_reg[0]);
-            res_reg[0] = 0x20;
-            res_reg[1] = SONY_BAD_DATA_ERR;
-            *res_size = 2;
-         }
-         abort_read();
-      }
-      else
-      {
-#if DEBUG
-         printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-         res_reg[0] = 0x20;
-         res_reg[1] = SONY_TIMEOUT_OP_ERR;
-         *res_size = 2;
-         abort_read();
-      }
-   }
-   else
-   {
-      input_data(buffer, bytesleft, nblocks, offset, skip);
-
-      /* Wait for the status from the drive. */
-      retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-      while ((retry_count > jiffies) && !(is_result_ready()))
-      {
-         while (handle_sony_cd_attention())
-            ;
-
-         sony_sleep();
-      }
-
-      if (!is_result_ready())
-      {
-#if DEBUG
-         printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-         res_reg[0] = 0x20;
-         res_reg[1] = SONY_TIMEOUT_OP_ERR;
-         *res_size = 2;
-         abort_read();
-      }
-      else
-      {
-         get_result(res_reg, res_size);
-
-	 /* If we got a buffer status, handle that. */
-         if ((res_reg[0] & 0xf0) == 0x50)
-         {
-
-            if (   (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
-                || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
-                || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT))
-            {
-	       /* The data was successful, but if data was read from
-		  the readahead  and it was bad, set the whole
-		  buffer as bad. */
-               if (readahead_bad)
-               {
-                  readahead_bad = 0;
-                  res_reg[0] = 0x20;
-                  res_reg[1] = SONY_BAD_DATA_ERR;
-                  *res_size = 2;
-               }
-            }
-            else
-            {
-               printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
-               res_reg[0] = 0x20;
-               res_reg[1] = SONY_BAD_DATA_ERR;
-               *res_size = 2;
-
-               /* Data is in the readahead buffer but an error was returned.
-                  Make sure future requests don't use the data. */
-               if (bytesleft != 2048)
-               {
-                  readahead_bad = 1;
-               }
-            }
-
-            /* Final transfer is done for read command, get final result. */
-            if (sony_blocks_left == 0)
-            {
-               get_result(res_reg, res_size);
-            }
-         }
-         else if ((res_reg[0] & 0xf0) != 0x20)
-         {
-            /* The drive gave me bad status, I don't know what to do.
-               Reset the driver and return an error. */
-            printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
-            restart_on_error();
-            res_reg[0] = 0x20;
-            res_reg[1] = SONY_BAD_DATA_ERR;
-            *res_size = 2;
-         }
-      }
-   }
-}
-
-/*
- * 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
- * bytes to help speed operations.  This especially helps since the OS
- * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
- * data access on a CD is done sequentially, this saves a lot of operations.
- */
-static void
-do_cdu31a_request(void)
-{
-   int block;
-   int nblock;
-   unsigned char res_reg[12];
-   unsigned int res_size;
-   int num_retries;
-   unsigned long flags;
-
-
-   /* 
-    * Make sure no one else is using the driver; wait for them
-    * to finish if it is so.
-    */
-   save_flags(flags);
-   cli();
-   while (sony_inuse)
-   {
-      interruptible_sleep_on(&sony_wait);
-      if (current->signal & ~current->blocked)
-      {
-         restore_flags(flags);
-         if (CURRENT && CURRENT->rq_status != RQ_INACTIVE)
-         {
-            end_request(0);
-         }
-         restore_flags(flags);
-         return;
-      }
-   }
-   sony_inuse = 1;
-   has_cd_task = current;
-
-   /* Get drive status before doing anything. */
-   while (handle_sony_cd_attention())
-      ;
-
-   sti();
-
-   /* If the timer is running, cancel it. */
-   if (cdu31a_abort_timer.next != NULL)
-   {
-      del_timer(&cdu31a_abort_timer);
-   }
-
-   while (1)
-   {
-cdu31a_request_startover:
-      /*
-       * The beginning here is stolen from the hard disk driver.  I hope
-       * it's right.
-       */
-      if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE)
-      {
-         goto end_do_cdu31a_request;
-      }
-
-      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);
-      }
-
-      /* I don't use INIT_REQUEST because it calls return, which would
-         return without unlocking the device.  It shouldn't matter,
-         but just to be safe... */
-      if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
-      {
-	 panic(DEVICE_NAME ": request list destroyed");
-      }
-      if (CURRENT->bh)
-      {
-	 if (!CURRENT->bh->b_lock)
-         {
-	    panic(DEVICE_NAME ": block not locked");
-         }
-      }
-
-      block = CURRENT->sector;
-      nblock = CURRENT->nr_sectors;
-
-      if (!sony_toc_read)
-      {
-	 printk("CDU31A: TOC not read\n");
-	 end_request(0);
-	 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:
-         /*
-          * If the block address is invalid or the request goes beyond the end of
-          * the media, return an error.
-          */
-#if 0
-	 if ((block / 4) < sony_toc.start_track_lba)
-	 {
-            printk("CDU31A: Request before beginning of media\n");
-            end_request(0);
-            goto cdu31a_request_startover;
-	 }
-#endif
-         if ((block / 4) >= sony_toc.lead_out_start_lba)
-         {
-            printk("CDU31A: Request past end of media\n");
-            end_request(0);
-            goto cdu31a_request_startover;
-         }
-         if (((block + nblock) / 4) >= sony_toc.lead_out_start_lba)
-         {
-            printk("CDU31A: Request past end of media\n");
-            end_request(0);
-            goto cdu31a_request_startover;
-         }
-
-         num_retries = 0;
-
-try_read_again:
-         while (handle_sony_cd_attention())
-            ;
-
-         if (!sony_toc_read)
-         {
-	    printk("CDU31A: TOC not read\n");
-	    end_request(0);
-	    goto cdu31a_request_startover;
-         }
-
-	 /* If no data is left to be read from the drive, start the
-	    next request. */
-         if (sony_blocks_left == 0)
-         {
-            if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
-            {
-               end_request(0);
-               goto cdu31a_request_startover;
-            }
-         }
-	 /* If the requested block is not the next one waiting in
-	    the driver, abort the current operation and start a
-	    new one. */
-         else if (block != sony_next_block)
-         {
-#if DEBUG
-            printk("CDU31A Warning: Read for block %d, expected %d\n",
-                   block,
-                   sony_next_block);
-#endif
-            abort_read();
-            if (!sony_toc_read)
-            {
-	       printk("CDU31A: TOC not read\n");
-	       end_request(0);
-               goto cdu31a_request_startover;
-            }
-            if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
-            {
-               printk("CDU31a: start request failed\n");
-               end_request(0);
-               goto cdu31a_request_startover;
-            }
-         }
-
-         read_data_block(CURRENT->buffer, block, nblock, res_reg, &res_size);
-         if (res_reg[0] == 0x20)
-         {
-            if (num_retries > MAX_CDU31A_RETRIES)
-            {
-               end_request(0);
-               goto cdu31a_request_startover;
-            }
-
-            num_retries++;
-            if (res_reg[1] == SONY_NOT_SPIN_ERR)
-            {
-               do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-            }
-            else
-            {
-               printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]);
-            }
-            goto try_read_again;
-         }
-         else
-         {
-            end_request(1);
-         }
-         break;
-            
-      case WRITE:
-         end_request(0);
-         break;
-            
-      default:
-         panic("CDU31A: Unknown cmd");
-      }
-   }
-
-end_do_cdu31a_request:
-#if 0
-   /* After finished, cancel any pending operations. */
-   abort_read();
-#else
-   /* Start a timer to time out after a while to disable
-      the read. */
-   cdu31a_abort_timer.expires = jiffies + 2*HZ; /* Wait 2 seconds */
-   add_timer(&cdu31a_abort_timer);
-#endif
-
-   has_cd_task = NULL;
-   sony_inuse = 0;
-   wake_up_interruptible(&sony_wait);
-   restore_flags(flags);
-}
-
-/* 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
- * successful.
- */
-static void
-sony_get_toc(void)
-{
-   unsigned char res_reg[2];
-   unsigned int res_size;
-   unsigned char parms[1];
-   int session;
-
-
-#if DEBUG
-   printk("Entering sony_get_toc\n");
-#endif
-
-   if (!sony_toc_read)
-   {
-      /* The idea here is we keep asking for sessions until the command
-	 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;
-      while (1)
-      {
-#if DEBUG
-         printk("Trying session %d\n", session);
-#endif
-	 parms[0] = session;
-	 do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD,
-			parms,
-			1, 
-			res_reg,
-			&res_size);
-
-#if DEBUG
-         printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]);
-#endif
-
-	 if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-	 {
-	    /* An error reading the TOC, this must be past the last session. */
-	    break;
-         }
-
-         session++;
-
-         /* Let's not get carried away... */
-         if (session > 20)
-         {
-            return;
-         }
-      }
-
-      session--;
-
-#if DEBUG
-      printk("Reading session %d\n", session);
-#endif
-
-      parms[0] = session;
-      do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
-		     parms,
-		     1, 
-		     (unsigned char *) &sony_toc, 
-		     &res_size);
-      if ((res_size < 2) || ((sony_toc.exec_status[0] & 0xf0) == 0x20))
-      {
-	 /* An error reading the TOC.  Return without sony_toc_read
-	    set. */
-	 return;
-      }
-      
-      sony_toc_read = 1;
-
-      /* For points that do not exist, move the data over them
-	 to the right location. */
-      if (sony_toc.pointb0 != 0xb0)
-      {
-	 mcovlp(((char *) &sony_toc) + 27,
-		((char *) &sony_toc) + 18,
-		res_size - 18);
-	 res_size += 9;
-      }
-      if (sony_toc.pointb1 != 0xb1)
-      {
-	 mcovlp(((char *) &sony_toc) + 36,
-		((char *) &sony_toc) + 27,
-		res_size - 27);
-	 res_size += 9;
-      }
-      if (sony_toc.pointb2 != 0xb2)
-      {
-	 mcovlp(((char *) &sony_toc) + 45,
-		((char *) &sony_toc) + 36,
-		res_size - 36);
-	 res_size += 9;
-      }
-      if (sony_toc.pointb3 != 0xb3)
-      {
-	 mcovlp(((char *) &sony_toc) + 54,
-		((char *) &sony_toc) + 45,
-		res_size - 45);
-	 res_size += 9;
-      }
-      if (sony_toc.pointb4 != 0xb4)
-      {
-	 mcovlp(((char *) &sony_toc) + 63,
-		((char *) &sony_toc) + 54,
-		res_size - 54);
-	 res_size += 9;
-      }
-      if (sony_toc.pointc0 != 0xc0)
-      {
-	 mcovlp(((char *) &sony_toc) + 72,
-		((char *) &sony_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("Disk session %d, start track: %d, stop track: %d\n",
-	  session,
-	  sony_toc.start_track_lba,
-	  sony_toc.lead_out_start_lba);
-#endif
-   }
-#if DEBUG
-   printk("Leaving sony_get_toc\n");
-#endif
-}
-
-
-/*
- * Search for a specific track in the table of contents.
- */
-static int
-find_track(int track)
-{
-   int i;
-   int num_tracks;
-
-
-   num_tracks = sony_toc.last_track_num - sony_toc.first_track_num + 1;
-   for (i = 0; i < num_tracks; i++)
-   {
-      if (sony_toc.tracks[i].track == track)
-      {
-         return i;
-      }
-   }
-
-   return -1;
-}
-
-
-/*
- * Read the subcode and put it int last_sony_subcode for future use.
- */
-static int
-read_subcode(void)
-{
-   unsigned int res_size;
-
-
-   do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD,
-                  NULL,
-                  0, 
-                  (unsigned char *) &last_sony_subcode, 
-                  &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]);
-      return -EIO;
-   }
-
-   return 0;
-}
-
-
-/*
- * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
- * the drive is playing, the subchannel needs to be read (since it would be
- * changing).  If the drive is paused or completed, the subcode information has
- * already been stored, just use that.  The ioctl call wants things in decimal
- * (not BCD), so all the conversions are done.
- */
-static int
-sony_get_subchnl_info(long arg)
-{
-   struct cdrom_subchnl schi;
-
-
-   /* Get attention stuff */
-   while (handle_sony_cd_attention())
-      ;
-
-   sony_get_toc();
-   if (!sony_toc_read)
-   {
-      return -EIO;
-   }
-
-   verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
-   verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
-
-   memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
-   
-   switch (sony_audio_status)
-   {
-   case CDROM_AUDIO_PLAY:
-      if (read_subcode() < 0)
-      {
-         return -EIO;
-      }
-      break;
-
-   case CDROM_AUDIO_PAUSED:
-   case CDROM_AUDIO_COMPLETED:
-      break;
-
-   case CDROM_AUDIO_NO_STATUS:
-      schi.cdsc_audiostatus = sony_audio_status;
-      memcpy_tofs((char *) arg, &schi, sizeof(schi));
-      return 0;
-      break;
-
-   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]);
-   }
-   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);
-   }
-   
-   memcpy_tofs((char *) arg, &schi, sizeof(schi));
-   return 0;
-}
-
-/* Get audio data from the drive.  This is fairly complex because I
-   am looking for status and data at the same time, but if I get status
-   then I just look for data.  I need to get the status immediately so
-   the switch from audio to data tracks will happen quickly. */
-static void
-read_audio_data(char          *buffer,
-                unsigned char res_reg[],
-                int           *res_size)
-{
-   unsigned int retry_count;
-   int result_read;
-
-
-   res_reg[0] = 0;
-   res_reg[1] = 0;
-   *res_size = 0;
-   result_read = 0;
-
-   /* Wait for the drive to tell us we have something */
-   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-continue_read_audio_wait:
-   while (   (retry_count > jiffies)
-          && !(is_data_ready())
-          && !(is_result_ready() || result_read))
-   {
-      while (handle_sony_cd_attention())
-         ;
-
-      sony_sleep();
-   }
-   if (!(is_data_ready()))
-   {
-      if (is_result_ready() && !result_read)
-      {
-         get_result(res_reg, res_size);
-
-         /* Read block status and continue waiting for data. */
-         if ((res_reg[0] & 0xf0) == 0x50)
-         {
-            result_read = 1;
-            goto continue_read_audio_wait;
-         }
-         /* Invalid data from the drive.  Shut down the operation. */
-         else if ((res_reg[0] & 0xf0) != 0x20)
-         {
-            printk("CDU31A: Got result that should have been error: %d\n",
-                   res_reg[0]);
-            res_reg[0] = 0x20;
-            res_reg[1] = SONY_BAD_DATA_ERR;
-            *res_size = 2;
-         }
-         abort_read();
-      }
-      else
-      {
-#if DEBUG
-         printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-         res_reg[0] = 0x20;
-         res_reg[1] = SONY_TIMEOUT_OP_ERR;
-         *res_size = 2;
-         abort_read();
-      }
-   }
-   else
-   {
-      clear_data_ready();
-
-      /* 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);
-      }
-      else
-      {
-         /* Audio gets the whole 2352 bytes. */
-         insb(sony_cd_read_reg, buffer, CD_FRAMESIZE_RAW);
-      }
-
-      /* If I haven't already gotten the result, get it now. */
-      if (!result_read)
-      {
-         /* Wait for the drive to tell us we have something */
-         retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-         while ((retry_count > jiffies) && !(is_result_ready()))
-         {
-            while (handle_sony_cd_attention())
-               ;
-
-            sony_sleep();
-         }
-
-         if (!is_result_ready())
-         {
-#if DEBUG
-            printk("CDU31A timeout out %d\n", __LINE__);
-#endif
-            res_reg[0] = 0x20;
-            res_reg[1] = SONY_TIMEOUT_OP_ERR;
-            *res_size = 2;
-            abort_read();
-            return;
-         }
-         else
-         {
-            get_result(res_reg, res_size);
-         }
-      }
-
-      if ((res_reg[0] & 0xf0) == 0x50)
-      {
-         if (   (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
-             || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
-             || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT)
-             || (res_reg[0] == SONY_NO_ERR_DETECTION_STAT))
-         {
-            /* Ok, nothing to do. */
-         }
-         else
-         {
-            printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
-            res_reg[0] = 0x20;
-            res_reg[1] = SONY_BAD_DATA_ERR;
-            *res_size = 2;
-         }
-      }
-      else if ((res_reg[0] & 0xf0) != 0x20)
-      {
-         /* The drive gave me bad status, I don't know what to do.
-            Reset the driver and return an error. */
-         printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
-         restart_on_error();
-         res_reg[0] = 0x20;
-         res_reg[1] = SONY_BAD_DATA_ERR;
-         *res_size = 2;
-      }
-   }
-}
-
-/* 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)
-{
-   int retval;
-   unsigned char params[2];
-   unsigned char res_reg[12];
-   unsigned int res_size;
-   unsigned int cframe;
-   unsigned long flags;
-
-   /* 
-    * Make sure no one else is using the driver; wait for them
-    * to finish if it is so.
-    */
-   save_flags(flags);
-   cli();
-   while (sony_inuse)
-   {
-      interruptible_sleep_on(&sony_wait);
-      if (current->signal & ~current->blocked)
-      {
-         restore_flags(flags);
-         return -EAGAIN;
-      }
-   }
-   sony_inuse = 1;
-   has_cd_task = current;
-   restore_flags(flags);
-
-   if (!sony_spun_up)
-   {
-      scd_open (inode, NULL);
-   }
-
-   /* Set the drive to do raw operations. */
-   params[0] = SONY_SD_DECODE_PARAM;
-   params[1] = 0x06 | sony_raw_data_mode;
-   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
-                  params,
-                  2,
-                  res_reg,
-                  &res_size);
-   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-   {
-      printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
-      return -EIO;
-   }
-
-   /* From here down, we have to goto exit_read_audio instead of returning
-      because the drive parameters have to be set back to data before
-      return. */
-
-   retval = 0;
-   /* start_request clears out any readahead data, so it should be safe. */
-   if (start_request(ra->addr.lba, ra->nframes, 1))
-   {
-      retval = -EIO;
-      goto exit_read_audio;
-   }
-
-   /* For every requested frame. */
-   cframe = 0;
-   while (cframe < ra->nframes)
-   {
-      read_audio_data(readahead_buffer, res_reg, &res_size);
-      if ((res_reg[0] & 0xf0) == 0x20)
-      {
-         if (res_reg[1] == SONY_BAD_DATA_ERR)
-         {
-            printk("CDU31A: Data error on audio sector %d\n",
-                   ra->addr.lba + cframe);
-         }
-         else if (res_reg[1] == SONY_ILL_TRACK_R_ERR)
-         {
-            /* Illegal track type, change track types and start over. */
-            sony_raw_data_mode = (sony_raw_data_mode) ? 0 : 1;
-
-            /* Set the drive mode. */
-            params[0] = SONY_SD_DECODE_PARAM;
-            params[1] = 0x06 | sony_raw_data_mode;
-            do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
-                           params,
-                           2,
-                           res_reg,
-                           &res_size);
-            if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-            {
-               printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
-               retval = -EIO;
-               goto exit_read_audio;
-            }
-
-            /* Restart the request on the current frame. */
-            if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1))
-            {
-               retval = -EIO;
-               goto exit_read_audio;
-            }
-
-            /* Don't go back to the top because don't want to get into
-               and infinite loop.  A lot of code gets duplicated, but
-               that's no big deal, I don't guess. */
-            read_audio_data(readahead_buffer, res_reg, &res_size);
-            if ((res_reg[0] & 0xf0) == 0x20)
-            {
-               if (res_reg[1] == SONY_BAD_DATA_ERR)
-               {
-                  printk("CDU31A: Data error on audio sector %d\n",
-                         ra->addr.lba + cframe);
-               }
-               else
-               {
-                  printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
-                         ra->addr.lba + cframe,
-                         res_reg[1]);
-                  retval = -EIO;
-                  goto exit_read_audio;
-               }
-            }
-            else
-            {
-               memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
-                           (char *) readahead_buffer,
-                           CD_FRAMESIZE_RAW);
-            }
-         }
-         else
-         {
-            printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
-                   ra->addr.lba + cframe,
-                   res_reg[1]);
-            retval = -EIO;
-            goto exit_read_audio;
-         }
-      }
-      else
-      {
-         memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
-                     (char *) readahead_buffer,
-                     CD_FRAMESIZE_RAW);
-      }
-
-      cframe++;
-   }
-
-   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]);
-      retval = -EIO;
-      goto exit_read_audio;
-   }
-
-exit_read_audio:
-
-   /* Set the drive mode back to the proper one for the disk. */
-   params[0] = SONY_SD_DECODE_PARAM;
-   if (!sony_xa_mode)
-   {
-      params[1] = 0x0f;
-   }
-   else
-   {
-      params[1] = 0x07;
-   }
-   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
-                  params,
-                  2,
-                  res_reg,
-                  &res_size);
-   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-   {
-      printk("CDU31A: Unable to reset decode params: 0x%2.2x\n", res_reg[1]);
-      return -EIO;
-   }
-
-   has_cd_task = NULL;
-   sony_inuse = 0;
-   wake_up_interruptible(&sony_wait);
-
-   return(retval);
-}
-
-static int
-do_sony_cd_cmd_chk(const char *name,
-                   unsigned char cmd,
-                   unsigned char *params,
-                   unsigned int num_params,
-		   unsigned char *result_buffer,
-                   unsigned int *result_size)
-{      
-      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);
-         return -EIO;
-      }
-      return 0;
-}
- 
-/*
- * The big ugly ioctl handler.
- */
-static int scd_ioctl(struct inode *inode,
-          struct file  *file,
-          unsigned int  cmd,
-          unsigned long arg)
-{
-   unsigned char res_reg[12];
-   unsigned int res_size;
-   unsigned char params[7];
-   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 */
-      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
-
-      /*
-       * Spin the drive down, ignoring the error if the disk was
-       * already not spinning.
-       */
-      sony_audio_status = CDROM_AUDIO_NO_STATUS;
-      return do_sony_cd_cmd_chk("STOP",SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-
-   case CDROMPAUSE:     /* Pause the drive */
-      if(do_sony_cd_cmd_chk("PAUSE", SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size))
-      	return -EIO;
-      /* Get the current position and save it for resuming */
-      if (read_subcode() < 0)
-      {
-         return -EIO;
-      }
-      cur_pos_msf[0] = last_sony_subcode.abs_msf[0];
-      cur_pos_msf[1] = last_sony_subcode.abs_msf[1];
-      cur_pos_msf[2] = last_sony_subcode.abs_msf[2];
-      sony_audio_status = CDROM_AUDIO_PAUSED;
-      return 0;
-      break;
-
-   case CDROMRESUME:    /* Start the drive after being paused */
-      if (sony_audio_status != CDROM_AUDIO_PAUSED)
-      {
-         return -EINVAL;
-      }
-      
-      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[0] = 0x03;
-      if(do_sony_cd_cmd_chk("RESUME",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0)
-      	return -EIO;
-      sony_audio_status = CDROM_AUDIO_PLAY;
-      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);
-      memcpy_fromfs(&(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[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];
-      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)
-         {
-            return -EIO;
-         }
-         
-         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);
-         memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
-      }
-      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;
-         
-         sony_get_toc();
-         if (!sony_toc_read)
-         {
-            return -EIO;
-         }
-         
-         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;
-         
-         memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry));
-         
-         /* Lead out is handled separately since it is special. */
-         if (loc_entry.cdte_track == CDROM_LEADOUT)
-         {
-            loc_entry.cdte_adr = sony_toc.address2;
-            loc_entry.cdte_ctrl = sony_toc.control2;
-            msf_val = sony_toc.lead_out_start_msf;
-         }
-         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;
-         }
-         
-         /* Logical buffer address or MSF format requested? */
-         if (loc_entry.cdte_format == CDROM_LBA)
-         {
-            loc_entry.cdte_addr.lba = msf_to_log(msf_val);
-         }
-         else if (loc_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));
-         }
-         memcpy_tofs(entry, &loc_entry, sizeof(*entry));
-      }
-      return 0;
-      break;
-
-   case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
-      {
-         struct cdrom_ti ti;
-         int track_idx;
-         
-         sony_get_toc();
-         if (!sony_toc_read)
-         {
-            return -EIO;
-         }
-         
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
-         if(i<0)
-         	return i;
-         
-         memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
-         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));
-         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];
-         
-         /*
-          * 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))
-         {
-            log_to_msf(msf_to_log(sony_toc.lead_out_start_msf)-1,
-                       &(params[4]));
-         }
-         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]));
-         }
-         params[0] = 0x03;
-         
-         do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-         
-         do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
-         
-         if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-         {
-            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]);
-            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];
-         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;
-         
-         memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
-         params[0] = SONY_SD_AUDIO_VOLUME;
-         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);
-
-      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
-				 raw data tracks. */
-      {
-         struct cdrom_read_audio ra;
-
-
-         sony_get_toc();
-         if (!sony_toc_read)
-         {
-            return -EIO;
-         }
-         
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ra));
-         if(i<0)
-         	return i;
-         memcpy_fromfs(&ra, (char *) arg, sizeof(ra));
-
-         i=verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes);
-         if(i<0)
-         	return i;
-
-         if (ra.addr_format == CDROM_LBA)
-         {
-            if (   (ra.addr.lba >= sony_toc.lead_out_start_lba)
-                || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba))
-            {
-               return -EINVAL;
-            }
-         }
-         else if (ra.addr_format == CDROM_MSF)
-         {
-            if (   (ra.addr.msf.minute >= 75)
-                || (ra.addr.msf.second >= 60)
-                || (ra.addr.msf.frame >= 75))
-            {
-               return -EINVAL;
-            }
-
-            ra.addr.lba = (  (ra.addr.msf.minute * 4500)
-                           + (ra.addr.msf.second * 75)
-                           + ra.addr.msf.frame);
-            if (   (ra.addr.lba >= sony_toc.lead_out_start_lba)
-                || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba))
-            {
-               return -EINVAL;
-            }
-
-            /* I know, this can go negative on an unsigned.  However,
-               the first thing done to the data is to add this value,
-               so this should compensate and allow direct msf access. */
-            ra.addr.lba -= LOG_START_OFFSET;
-         }
-         else
-         {
-            return -EINVAL;
-         }
-
-         return(read_audio(&ra, inode));
-      }
-      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)
-{
-   unsigned char res_reg[12];
-   unsigned int res_size;
-   int num_spin_ups;
-   unsigned char params[2];
-
-
-   if ((filp) && filp->f_mode & 2)
-      return -EROFS;
-
-   if (!sony_spun_up)
-   {
-      num_spin_ups = 0;
-
-respinup_on_open:
-      do_sony_cd_cmd(SONY_SPIN_UP_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)))
-      {
-         printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
-         return -EIO;
-      }
-      
-      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 is already playing, it's ok.  */
-         if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
-         {
-            goto drive_spinning;
-         }
-
-         /* 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 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);
-         
-         return -EIO;
-      }
-
-      sony_get_toc();
-      if (!sony_toc_read)
-      {
-         do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-         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)
-          && (!is_double_speed))
-      {
-         params[0] = SONY_SD_DECODE_PARAM;
-         params[1] = 0x07;
-         do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
-                        params,
-                        2,
-                        res_reg,
-                        &res_size);
-         if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-         {
-            printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]);
-         }
-         sony_xa_mode = 1;
-      }
-      /* A non-XA disk.  Set the parms back if necessary. */
-      else if (sony_xa_mode)
-      {
-         params[0] = SONY_SD_DECODE_PARAM;
-         params[1] = 0x0f;
-         do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
-                        params,
-                        2,
-                        res_reg,
-                        &res_size);
-         if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
-         {
-            printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]);
-         }
-         sony_xa_mode = 0;
-      }
-
-      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++;
-
-   return 0;
-}
-
-
-/*
- * 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 void
-scd_release(struct inode *inode,
-         struct file *filp)
-{
-   unsigned char res_reg[12];
-   unsigned int  res_size;
-
-
-   if (sony_usage > 0)
-   {
-      sony_usage--;
-   }
-   if (sony_usage == 0)
-   {
-      sync_dev(inode->i_rdev);
-      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-
-      sony_spun_up = 0;
-   }
-}
-
-
-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,                   /* select */
-   scd_ioctl,              /* ioctl */
-   NULL,                   /* mmap */
-   scd_open,               /* open */
-   scd_release,            /* release */
-   NULL,                   /* fsync */
-   NULL,                   /* fasync */
-   scd_disk_change,        /* media_change */
-   NULL                    /* revalidate */
-};
-
-
-/* The different types of disc loading mechanisms supported */
-static const char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
-
-static void
-get_drive_configuration(unsigned short base_io,
-                        unsigned char res_reg[],
-                        unsigned int *res_size)
-{
-   int retry_count;
-
-
-   /* Set the base address */
-   cdu31a_port = base_io;
-
-   /* Set up all the register locations */
-   sony_cd_cmd_reg = cdu31a_port + SONY_CMD_REG_OFFSET;
-   sony_cd_param_reg = cdu31a_port + SONY_PARAM_REG_OFFSET;
-   sony_cd_write_reg = cdu31a_port + SONY_WRITE_REG_OFFSET;
-   sony_cd_control_reg = cdu31a_port + SONY_CONTROL_REG_OFFSET;
-   sony_cd_status_reg = cdu31a_port + SONY_STATUS_REG_OFFSET;
-   sony_cd_result_reg = cdu31a_port + SONY_RESULT_REG_OFFSET;
-   sony_cd_read_reg = cdu31a_port + SONY_READ_REG_OFFSET;
-   sony_cd_fifost_reg = cdu31a_port + SONY_FIFOST_REG_OFFSET;
-
-   /*
-    * Check to see if anything exists at the status register location.
-    * I don't know if this is a good way to check, but it seems to work
-    * ok for me.
-    */
-   if (read_status_register() != 0xff)
-   {
-      /*
-       * Reset the drive and wait for attention from it (to say it's reset).
-       * If you don't wait, the next operation will probably fail.
-       */
-      reset_drive();
-      retry_count = jiffies + SONY_RESET_TIMEOUT;
-      while ((retry_count > jiffies) && (!is_attention()))
-      {
-         sony_sleep();
-      }
-
-#if 0
-      /* If attention is never seen probably not a CDU31a present */
-      if (!is_attention())
-      {
-         res_reg[0] = 0x20;
-         return;
-      }
-#endif
-
-      /*
-       * Get the drive configuration.
-       */
-      do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD,
-                     NULL,
-                     0,
-                     (unsigned char *) res_reg,
-                     res_size);
-      return;
-   }
-
-   /* Return an error */
-   res_reg[0] = 0x20;
-}
-
-/*
- * Set up base I/O and interrupts, called from main.c.
- */
-void
-cdu31a_setup(char *strings,
-	     int  *ints)
-{
-   if (ints[0] > 0)
-   {
-      cdu31a_port = ints[1];
-   }
-   if (ints[0] > 1)
-   {
-      cdu31a_irq = ints[2];
-   }
-   if ((strings != NULL) && (*strings != '\0'))
-   {
-      if (strcmp(strings, "PAS") == 0)
-      {
-	 sony_pas_init = 1;
-      }
-      else
-      {
-	 printk("CDU31A: Unknown interface type: %s\n", strings);
-      }
-   }
-}
-
-static int cdu31a_block_size;
-
-/*
- * Initialize the driver.
- */
-#ifdef MODULE
-#define cdu31a_init init_module
-#endif
-
-int
-cdu31a_init(void)
-{
-   struct s_sony_drive_config drive_config;
-   unsigned int res_size;
-   int i;
-   int drive_found;
-   int tmp_irq;
-
-
-   /*
-    * According to Alex Freed (freed@europa.orion.adobe.com), this is
-    * required for the Fusion CD-16 package.  If the sound driver is
-    * loaded, it should work fine, but just in case...
-    *
-    * The following turn on the CD-ROM interface for a Fusion CD-16.
-    */
-   if (sony_pas_init)
-   {
-      outb(0xbc, 0x9a01);
-      outb(0xe2, 0x9a01);
-   }
-
-   drive_found = 0;
-
-   /* Setting the base I/O address to 0xffff will disable it. */
-   if (cdu31a_port == 0xffff)
-   {
-   }
-   else if (cdu31a_port != 0)
-   {
-      tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */
-      cdu31a_irq = 0;
-
-      get_drive_configuration(cdu31a_port,
-			      drive_config.exec_status,
-			      &res_size);
-      if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
-      {
-	 drive_found = 1;
-      }
-
-      cdu31a_irq = tmp_irq;
-   }
-   else
-   {
-      cdu31a_irq = 0;
-      i = 0;
-      while (   (cdu31a_addresses[i].base != 0)
-	     && (!drive_found))
-      {
-	 if (check_region(cdu31a_addresses[i].base, 4)) {
-	    i++;
-	    continue;
-	 }
-	 get_drive_configuration(cdu31a_addresses[i].base,
-				 drive_config.exec_status,
-				 &res_size);
-	 if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
-	 {
-	    drive_found = 1;
-	    cdu31a_irq = cdu31a_addresses[i].int_num;
-	 }
-	 else
-	 {
-	    i++;
-	 }
-      }
-   }
-
-   if (drive_found)
-   {
-      request_region(cdu31a_port, 4,"cdu31a");
-      
-      if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
-      {
-	 printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
-	 return -EIO;
-      }
-
-      if (SONY_HWC_DOUBLE_SPEED(drive_config))
-      {
-	 is_double_speed = 1;
-      }
-
-      tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */
-      cdu31a_irq = 0;
-
-      set_drive_params();
-
-      cdu31a_irq = tmp_irq;
-      
-      if (cdu31a_irq > 0)
-      {
-	 if (request_irq(cdu31a_irq, cdu31a_interrupt, SA_INTERRUPT, "cdu31a"))
-	 {
-	    printk("Unable to grab IRQ%d for the CDU31A driver\n", cdu31a_irq);
-	    cdu31a_irq = 0;
-	 }
-      }
-      
-      printk("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("  Capabilities: %s",
-	     load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
-      if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
-      {
-	 printk(", audio");
-      }
-      if (SONY_HWC_EJECT(drive_config))
-      {
-	 printk(", eject");
-      }
-      if (SONY_HWC_LED_SUPPORT(drive_config))
-      {
-	 printk(", LED");
-      }
-      if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
-      {
-	 printk(", elec. Vol");
-      }
-      if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
-      {
-	 printk(", sep. Vol");
-      }
-      if (is_double_speed)
-      {
-	 printk(", double speed");
-      }
-      if (cdu31a_irq > 0)
-      {
-	 printk(", irq %d", cdu31a_irq);
-      }
-      printk("\n");
-
-      blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-      read_ahead[MAJOR_NR] = CDU31A_READAHEAD;
-      cdu31a_block_size = 1024; /* 1kB default block size */
-      /* use 'mount -o block=2048' */
-      blksize_size[MAJOR_NR] = &cdu31a_block_size;
-      
-      cdu31a_abort_timer.next = NULL;
-      cdu31a_abort_timer.prev = NULL;
-      cdu31a_abort_timer.function = handle_abort_timeout;
-   }
-
-
-   disk_changed = 1;
-   
-   if (drive_found)
-   {
-      return(0);
-   }
-   else
-   {
-      return -EIO;
-   }
-}
-
-#ifdef MODULE
-void
-cleanup_module(void)
-{
-   if (sony_usage != 0)
-   {
-      printk("cdu31a module in use - can't remove it.\n");
-      return;
-   }
-
-   if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL))    
-   {
-      printk("Can't unregister cdu31a\n");
-      return;
-   }
-   release_region(cdu31a_port,4);
-   printk("cdu31a module released.\n");
-}   
-#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/block/cm206.c linux/drivers/block/cm206.c
--- v1.3.35/linux/drivers/block/cm206.c	Tue Oct 10 18:46:31 1995
+++ linux/drivers/block/cm206.c	Thu Jan  1 02:00:00 1970
@@ -1,1249 +0,0 @@
-/* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card.
-   Copyright (c) 1995 David van Leeuwen.
-   
-     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:
- Started 25 jan 1994. Waiting for documentation...
- 22 feb 1995: 0.1a first reasonably safe polling driver.
-	      Two major bugs, one in read_sector and one in 
-	      do_cm206_request, happened to cancel!
- 25 feb 1995: 0.2a first reasonable interrupt driven version of above.
-              uart writes are still done in polling mode. 
- 25 feb 1995: 0.21a writes also in interrupt mode, still some
-	      small bugs to be found... Larger buffer. 
-  2 mrt 1995: 0.22 Bug found (cd-> nowhere, interrupt was called in
-              initialization), read_ahead of 16. Timeouts implemented.
-	      unclear if they do something...
-  7 mrt 1995: 0.23 Start of background read-ahead.
- 18 mrt 1995: 0.24 Working background read-ahead. (still problems)
- 26 mrt 1995: 0.25 Multi-session ioctl added (kernel v1.2).
-              Statistics implemented, though separate stats206.h.
-	      Accessible trough ioctl 0x1000 (just a number).
-	      Hard to choose between v1.2 development and 1.1.75.
-	      Bottom-half doesn't work with 1.2...
-	      0.25a: fixed... typo. Still problems...
-  1 apr 1995: 0.26 Module support added. Most bugs found. Use kernel 1.2.n.
-  5 apr 1995: 0.27 Auto-probe for the adapter card base address.
-              Auto-probe for the adaptor card irq line.
-  7 apr 1995: 0.28 Added lilo setup support for base address and irq.
-              Use major number 32 (not in this source), officially
-	      assigned to this driver.
-  9 apr 1995: 0.29 Added very limited audio support. Toc_header, stop, pause,
-              resume, eject. Play_track ignores track info, because we can't 
-	      read a table-of-contents entry. Toc_entry is implemented
-	      as a `placebo' function: always returns start of disc. 
-  3 may 1995: 0.30 Audio support completed. The get_toc_entry function
-              is implemented as a binary search. 
- 15 may 1995: 0.31 More work on audio stuff. Workman is not easy to 
-              satisfy; changed binary search into linear search.
-	      Auto-probe for base address somewhat relaxed.
-  1 jun 1995: 0.32 Removed probe_irq_on/off for module version.
- 10 jun 1995: 0.33 Workman still behaves funny, but you should be
-              able to eject and substitute another disc.
-
- An adaption of 0.33 is included in linux-1.3.7 by Eberhard Moenkeberg
-
- 18 jul 1996: 0.34 Patch by Heiko Eissfeldt included, mainly considering 
-              verify_area's in the ioctls. Some bugs introduced by 
-	      EM considering the base port and irq fixed. 
- * 
- * Parts of the code are based upon lmscd.c written by Kai Petzke,
- * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin
- * Harriss, but any off-the-shelf dynamic programming algorithm won't
- * be able to find them.
- *
- * The cm206 drive interface and the cm260 adapter card seem to be 
- * sufficiently different from their cm205/cm250 counterparts
- * in order to write a complete new driver.
- * 
- * I call all routines connected to the Linux kernel something
- * with `cm206' in it, as this stuff is too series-dependent. 
- * 
- * Currently, my limited knowledge is based on:
- * - The Linux Kernel Hacker's guide, v. 0.5 , by Michael J. Johnson
- * - Linux Kernel Programmierung, by Michael Beck and others
- * - Philips/LMS cm206 and cm226 product specification
- * - Philips/LMS cm260 product specification
- *
- *                       David van Leeuwen, david@tm.tno.nl.  */
-#define VERSION "0.34"
-
-#ifdef MODULE			/* OK, so some of this is stolen */
-#include <linux/module.h>	
-#include <linux/version.h>
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[]=UTS_RELEASE;
-#endif
-#else 
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif MODULE
-
-#include <linux/errno.h>	/* These include what we really need */
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-
-#include <asm/io.h>
-
-#define MAJOR_NR CM206_CDROM_MAJOR
-#include "blk.h"
-#include <linux/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 
-   parameter `auto'.
-*/
-static int auto_probe=1;	/* Yes, why not? */
-
-static int cm206_base = CM206_BASE;
-static int cm206_irq = CM206_IRQ; 
-
-#undef DEBUG
-#undef DEBUG_SECTORS
-#define STATISTICS
-#undef AUTO_PROBE_MODULE
-
-#define POLLOOP 10000
-#define READ_AHEAD 1		/* defines private buffer, waste! */
-#define BACK_AHEAD 1		/* defines adapter-read ahead */
-#define DATA_TIMEOUT (3*HZ)	/* measured in jiffies (10 ms) */
-#define UART_TIMEOUT (5*HZ/100)
-#define DSB_TIMEOUT (7*HZ)	/* time for the slowest command to finish */
-
-#define RAW_SECTOR_SIZE 2352	/* ok, is also defined in cdrom.h */
-#define ISO_SECTOR_SIZE 2048
-
-#ifdef STATISTICS		/* keep track of errors in counters */
-#include <linux/stats206.h>
-#define stats(i) ++cd->stats[st_ ## i]; \
-                 cd->last_stat[st_ ## i] = cd->stat_counter++;
-#else
-#define stats(i) (void) 0
-#endif
-
-#ifdef DEBUG			/* from lmscd.c */
-#define debug(a) printk a
-#else
-#define debug(a) (void) 0
-#endif
-
-typedef unsigned char uch;	/* 8-bits */
-typedef unsigned short ush;	/* 16-bits */
-
-struct toc_struct{
-  uch track, fsm[3], q0;
-};
-
-struct cm206_struct {
-  ush intr_ds;	 /* data status read on last interrupt */
-  ush intr_ls;	 /* uart line status read on last interrupt*/
-  uch intr_ur;			/* uart receive buffer */
-  uch dsb, cc;	 /* drive status byte and condition (error) code */
-  uch fool;
-  int command;			/* command to be written to te uart */
-  int openfiles;
-  ush sector[READ_AHEAD*RAW_SECTOR_SIZE/2]; /* buffered cd-sector */
-  int sector_first, sector_last;	/* range of these sector */
-  struct wait_queue * uart;	/* wait for interrupt */
-  struct wait_queue * data;
-  struct timer_list timer;	/* time-out */
-  char timed_out;
-  signed char max_sectors;
-  char wait_back;		/* we're waiting for a background-read */
-  char background;		/* is a read going on in the background? */
-  int adapter_first;		/* if so, that's the starting sector */
-  int adapter_last;
-  char fifo_overflowed;
-  uch disc_status[7];		/* result of get_disc_status command */
-#ifdef STATISTICS
-  int stats[NR_STATS];
-  int last_stat[NR_STATS];	/* `time' at which stat was stat */
-  int stat_counter;
-#endif  
-  struct toc_struct toc[101];	/* The whole table of contents + lead-out */
-  uch q[10];			/* Last read q-channel info */
-  uch audio_status[5];		/* last read position on pause */
-};
-
-#define DISC_STATUS cd->disc_status[0]
-#define FIRST_TRACK cd->disc_status[1]
-#define LAST_TRACK cd->disc_status[2]
-#define PAUSED cd->audio_status[0] /* misuse this memory byte! */
-#define PLAY_TO cd->toc[0]	/* toc[0] records end-time in play */
-
-static struct cm206_struct * cd;
-
-/* First, we define some polling functions. These are actually
-   only being used in the initialization. */
-
-void send_command_polled(int command)
-{
-  int loop=POLLOOP;
-  while (!(inw(r_line_status) & ls_transmitter_buffer_empty) && loop>0) 
-    --loop;
-  outw(command, r_uart_transmit);
-}
-
-uch receive_echo_polled(void)
-{
-  int loop=POLLOOP;
-  while (!(inw(r_line_status) & ls_receive_buffer_full) && loop>0) --loop;
-  return ((uch) inw(r_uart_receive));
-}
-
-uch send_receive_polled(int command)
-{
-  send_command_polled(command);
-  return receive_echo_polled();
-}
-
-/* The interrupt handler. When the cm260 generates an interrupt, very
-   much care has to be taken in reading out the registers in the right
-   order; in case of a receive_buffer_full interrupt, first the
-   uart_receive must be read, and then the line status again to
-   de-assert the interrupt line. It took me a couple of hours to find
-   this out:-( 
-
-   The function reset_cm206 appears to cause an interrupt, because
-   pulling up the INIT line clears both the uart-write-buffer /and/
-   the uart-write-buffer-empty mask. We call this a `lost interrupt,'
-   as there seems so reason for this to happen.
-*/
-
-static void cm206_interrupt(int sig, struct pt_regs * regs) /* you rang? */
-{
-  volatile ush fool;
-    cd->intr_ds = inw(r_data_status); /* resets data_ready, data_error,
-					 crc_error, sync_error, toc_ready 
-					 interrupts */
-    cd->intr_ls = inw(r_line_status); /* resets overrun bit */
-    /* receive buffer full? */
-    if (cd->intr_ls & ls_receive_buffer_full) {	
-      cd->intr_ur = inb(r_uart_receive); /* get order right! */
-      cd->intr_ls = inw(r_line_status); /* resets rbf interrupt */
-      if (!cd->background && cd->uart) wake_up_interruptible(&cd->uart);
-    }
-    /* data ready in fifo? */
-    else if (cd->intr_ds & ds_data_ready) { 
-      if (cd->background) ++cd->adapter_last;
-      if ((cd->wait_back || !cd->background) && cd->data) 
-	  wake_up_interruptible(&cd->data);
-      stats(data_ready);
-    }
-    /* ready to issue a write command? */
-    else if (cd->command && cd->intr_ls & ls_transmitter_buffer_empty) {
-      outw(dc_normal | (inw(r_data_status) & 0x7f), r_data_control);
-      outw(cd->command, r_uart_transmit);
-      cd->command=0;
-      if (!cd->background) wake_up_interruptible(&cd->uart);
-    }
-    /* now treat errors (at least, identify them for debugging) */
-    else if (cd->intr_ds & ds_fifo_overflow) {
-      debug(("Fifo overflow at sectors 0x%x\n", cd->sector_first));
-      fool = inw(r_fifo_output_buffer);	/* de-assert the interrupt */
-      cd->fifo_overflowed=1;	/* signal one word less should be read */
-      stats(fifo_overflow);
-    }
-    else if (cd->intr_ds & ds_data_error) {
-      debug(("Data error at sector 0x%x\n", cd->sector_first));
-      stats(data_error);
-    }
-    else if (cd->intr_ds & ds_crc_error) {
-      debug(("CRC error at sector 0x%x\n", cd->sector_first));
-      stats(crc_error);
-    }
-    else if (cd->intr_ds & ds_sync_error) {
-      debug(("Sync at sector 0x%x\n", cd->sector_first));
-      stats(sync_error);
-    }
-    else if (cd->intr_ds & ds_toc_ready) {
-				/* do something appropiate */
-    }
-    /* couldn't see why this interrupt, maybe due to init */
-    else {			
-      outw(dc_normal | READ_AHEAD, r_data_control);
-      stats(lost_intr);
-    }
-  if (cd->background && (cd->adapter_last-cd->adapter_first == cd->max_sectors
-      || cd->fifo_overflowed))
-    mark_bh(CM206_BH);	/* issue a stop read command */
-  stats(interrupt);
-}
-
-/* we have put the address of the wait queue in who */
-void cm206_timeout(unsigned long who)
-{
-  cd->timed_out = 1;
-  wake_up_interruptible((struct wait_queue **) who);
-}
-
-/* This function returns 1 if a timeout occurred, 0 if an interrupt
-   happened */
-int sleep_or_timeout(struct wait_queue ** wait, int timeout)
-{
-  cd->timer.data=(unsigned long) wait;
-  cd->timer.expires = jiffies + timeout;
-  add_timer(&cd->timer);
-  interruptible_sleep_on(wait);
-  del_timer(&cd->timer);
-  if (cd->timed_out) {
-    cd->timed_out = 0;
-    return 1;
-  }
-  else return 0;
-}
-
-void cm206_delay(int jiffies) 
-{
-  struct wait_queue * wait = NULL;
-  sleep_or_timeout(&wait, jiffies);
-}
-
-void send_command(int command)
-{
-  if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) {
-    cd->command = command;
-    cli();			/* don't interrupt before sleep */
-    outw(dc_mask_sync_error | dc_no_stop_on_error | 
-	 (inw(r_data_status) & 0x7f), r_data_control);
-    /* interrupt routine sends command */
-    if (sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
-      debug(("Time out on write-buffer\n"));
-      stats(write_timeout);
-      outw(command, r_uart_transmit);
-    }
-  }
-  else outw(command, r_uart_transmit);
-}
-
-uch receive_echo(void)
-{
-  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
-      sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
-    debug(("Time out on receive-buffer\n"));
-    stats(receive_timeout);
-    return ((uch) inw(r_uart_receive));
-  }
-  return cd->intr_ur;
-}
-
-inline uch send_receive(int command)
-{
-  send_command(command);
-  return receive_echo();
-}
-
-uch wait_dsb(void)
-{
-  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
-      sleep_or_timeout(&cd->uart, DSB_TIMEOUT)) {
-    debug(("Time out on Drive Status Byte\n"));
-    stats(dsb_timeout);
-    return ((uch) inw(r_uart_receive));
-  }
-  return cd->intr_ur;
-}
-
-int type_0_command(int command, int expect_dsb)
-{
-  int e;
-  if (command != (e=send_receive(command))) {
-    debug(("command 0x%x echoed as 0x%x\n", command, e));
-    stats(echo);
-    return -1;
-  }
-  if (expect_dsb) {
-    cd->dsb = wait_dsb();	/* wait for command to finish */
-  }
-  return 0;
-}
-
-int type_1_command(int command, int bytes, uch * status) /* returns info */
-{
-  int i;
-  if (type_0_command(command,0)) return -1;
-  for(i=0; i<bytes; i++) 
-    status[i] = send_receive(c_gimme);
-  return 0;
-}  
-
-/* This function resets the adapter card. We'd better not do this too */
-/* often, because it tends to generate `lost interrupts.' */
-void reset_cm260(void)
-{
-  outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control);
-  udelay(10);			/* 3.3 mu sec minimum */
-  outw(dc_normal | READ_AHEAD, r_data_control);
-}
-
-/* fsm: frame-sec-min from linear address */
-void fsm(int lba, uch * fsm) 
-{
-  fsm[0] = lba % 75;
-  lba /= 75; lba += 2;
-  fsm[1] = lba % 60; fsm[2] = lba / 60;
-}
-
-inline int fsm2lba(uch * fsm) 
-{
-  return fsm[0] + 75*(fsm[1]-2 + 60*fsm[2]);
-}
-
-inline int f_s_m2lba(uch f, uch s, uch m)
-{
-  return f + 75*(s-2 + 60*m);
-}
-
-int start_read(int start) 
-{
-  uch read_sector[4] = {c_read_data, };
-  int i, e;
-
-  fsm(start, &read_sector[1]);
-  for (i=0; i<4; i++) 
-    if (read_sector[i] != (e=send_receive(read_sector[i]))) {
-      debug(("read_sector: %x echoes %x\n", read_sector[i], e));
-      stats(echo);
-      return -1;
-    }
-  return 0;
-}
-
-int stop_read(void)
-{
-  type_0_command(c_stop,0);
-  if(receive_echo() != 0xff) {
-    debug(("c_stop didn't send 0xff\n"));
-    stats(stop_0xff);
-    return -1;
-  }
-  return 0;
-}  
-
-/* This function starts to read sectors in adapter memory, the
-   interrupt routine should stop the read. In fact, the bottom_half
-   routine takes care of this. Set a flag `background' in the cd
-   struct to indicate the process. */
-
-int read_background(int start, int reading)
-{
-  if (cd->background) return -1; /* can't do twice */
-  outw(dc_normal | BACK_AHEAD, r_data_control);
-  if (!reading && start_read(start)) return -2;
-  cd->adapter_first = cd->adapter_last = start; 
-  cd->background = 1;		/* flag a read is going on */
-  return 0;
-}
-
-int read_sector(int start)
-{
-  if (cd->background) {
-    cd->background=0;
-    cd->adapter_last = -1;	/* invalidate adapter memory */
-    stop_read();
-  }
-  cd->fifo_overflowed=0;
-  reset_cm260();		/* empty fifo etc. */
-  if (start_read(start)) return -1;
-  if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
-    debug(("Read timed out sector 0x%x\n", start));
-    stats(read_timeout);
-    stop_read();
-    return -3;		
-  }
-  insw(r_fifo_output_buffer, cd->sector, READ_AHEAD*RAW_SECTOR_SIZE/2);
-  if (read_background(start+READ_AHEAD,1)) stats(read_background);
-  cd->sector_first = start; cd->sector_last = start+READ_AHEAD;
-  stats(read_restarted);
-  return 0;
-}
-
-/* The function of bottom-half is to send a stop command to the drive
-   This isn't easy because the routine is not `owned' by any process;
-   we can't go to sleep! The variable cd->background gives the status:
-   0 no read pending
-   1 a read is pending
-   2 c_stop waits for write_buffer_empty
-   3 c_stop waits for receive_buffer_full: echo
-   4 c_stop waits for receive_buffer_full: 0xff
-*/
-
-void cm206_bh(void * unused)
-{
-  debug(("bh: %d\n", cd->background));
-  switch (cd->background) {
-  case 1:
-    stats(bh);
-    if (!(cd->intr_ls & ls_transmitter_buffer_empty)) {
-      cd->command = c_stop;
-      outw(dc_mask_sync_error | dc_no_stop_on_error | 
-	   (inw(r_data_status) & 0x7f), r_data_control);
-      cd->background=2;
-      break;			/* we'd better not time-out here! */
-    }
-    else outw(c_stop, r_uart_transmit);
-    /* fall into case 2: */
-  case 2:			
-    /* the write has been satisfied by interrupt routine */
-    cd->background=3;
-    break;
-  case 3:
-    if (cd->intr_ur != c_stop) {
-      debug(("cm206_bh: c_stop echoed 0x%x\n", cd->intr_ur));
-      stats(echo);
-    }
-    cd->background++;
-    break;
-  case 4:
-    if (cd->intr_ur != 0xff) {
-      debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->intr_ur));
-      stats(stop_0xff);
-    }
-    cd->background=0;
-  }
-}
-
-void get_drive_status(void)
-{
-  uch status[2];
-  type_1_command(c_drive_status, 2, status); /* this might be done faster */
-  cd->dsb=status[0];
-  cd->cc=status[1];
-}
-
-void get_disc_status(void)
-{
-  if (type_1_command(c_disc_status, 7, cd->disc_status)) {
-    debug(("get_disc_status: error\n"));
-  }
-}
-
-static int cm206_open(struct inode *ip, struct file *fp)
-{
-  if (!cd->openfiles) {
-    cd->background=0;
-    reset_cm260();
-    cd->adapter_last = -1;	/* invalidate adapter memory */
-    cd->sector_last = -1;
-    get_drive_status();
-    if (cd->dsb & dsb_tray_not_closed) {
-      int i=0;
-      type_0_command(c_close_tray, 1);
-      while (i++<10 && cd->dsb & dsb_drive_not_ready) {
-	cm206_delay(100);
-	get_drive_status();
-      }
-    }
-    if (cd->dsb & (dsb_not_useful)) return -EIO;
-    if (!(cd->dsb & dsb_disc_present)) return -ENODATA;
-    if (cd->dsb & dsb_possible_media_change) {
-      memset(cd->toc, 0, sizeof(cd->toc));
-      memset(cd->audio_status, 0, sizeof(cd->audio_status));
-    }
-    get_disc_status();
-    type_0_command(c_lock_tray,1);
-    if (!(cd->dsb & dsb_tray_locked)) {
-      debug(("Couldn't lock tray\n"));
-    }
-#if 0
-    if (!(DISC_STATUS & cds_all_audio))
-      read_background(16,0);	/* do something useful */
-#endif
-  }
-  ++cd->openfiles; MOD_INC_USE_COUNT;
-  stats(open);
-  return 0;
-}
-
-static void cm206_release(struct inode *ip, struct file *fp)
-{
-  if (cd->openfiles==1) {
-    if (cd->background) {
-      cd->background=0;
-      stop_read();
-    }
-    type_0_command(c_unlock_tray,1);
-    cd->sector_last = -1;	/* Make our internal buffer invalid */
-    FIRST_TRACK = 0;	/* No valid disc status */
-    sync_dev(ip -> i_rdev);	/* These two lines are stolen */
-    invalidate_buffers(ip -> i_rdev);
-  }
-  --cd->openfiles; MOD_DEC_USE_COUNT;
-}
-
-/* Empty buffer empties $sectors$ sectors of the adapter card buffer,
- * and then reads a sector in kernel memory.  */
-void empty_buffer(int sectors) 
-{
-  while (sectors>=0) {
-    insw(r_fifo_output_buffer, cd->sector + cd->fifo_overflowed, 
-	 RAW_SECTOR_SIZE/2 - cd->fifo_overflowed);
-    --sectors;
-    ++cd->adapter_first;	/* update the current adapter sector */
-    cd->fifo_overflowed=0;	/* reset overflow bit */
-    stats(sector_transferred);
-  } 
-  cd->sector_first=cd->adapter_first-1;
-  cd->sector_last=cd->adapter_first; /* update the buffer sector */
-}
-
-/* try_adapter. This function determines of the requested sector is is
-   in adapter memory, or will appear there soon. Returns 0 upon
-   success */
-int try_adapter(int sector)
-{
-  if (cd->adapter_first <= sector && sector < cd->adapter_last) { 
-    /* sector is in adapter memory */
-    empty_buffer(sector - cd->adapter_first);
-    return 0;
-  }
-  else if (cd->background==1 && cd->adapter_first <= sector
-	   && sector < cd->adapter_first+cd->max_sectors) {
-    /* a read is going on, we can wait for it */
-    cd->wait_back=1;
-    while (sector >= cd->adapter_last) {
-      if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
-	debug(("Timed out during background wait: %d %d %d %d\n", sector, 
-	       cd->adapter_last, cd->adapter_first, cd->background));
-	stats(back_read_timeout);
-	cd->wait_back=0;
-	return -1;
-      }
-    }
-    cd->wait_back=0;
-    empty_buffer(sector - cd->adapter_first);
-    return 0;
-  }
-  else return -2;
-}
-
-/* This is not a very smart implementation. We could optimize for 
-   consecutive block numbers. I'm not convinced this would really
-   bring down the processor load. */
-static void do_cm206_request(void)
-{
-  long int i, cd_sec_no;
-  int quarter, error; 
-  uch * source, * dest;
-  
-  while(1) {	 /* repeat until all requests have been satisfied */
-    INIT_REQUEST;
-    if (CURRENT == NULL || CURRENT->rq_status == RQ_INACTIVE)
-      return;
-    if (CURRENT->cmd != READ) {
-      debug(("Non-read command %d on cdrom\n", CURRENT->cmd));
-      end_request(0);
-      continue;
-    }
-    error=0;
-    for (i=0; i<CURRENT->nr_sectors; i++) {
-      cd_sec_no = (CURRENT->sector+i)/4; /* 4 times 512 bytes */
-      quarter = (CURRENT->sector+i) % 4; 
-      dest = CURRENT->buffer + i*512;
-      /* is already in buffer memory? */
-      if (cd->sector_first <= cd_sec_no && cd_sec_no < cd->sector_last) {
-	source = ((uch *) cd->sector) + 16 + 
-	  quarter*512 + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE;
- 	memcpy(dest, source, 512); 
-      }
-      else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) {
-	source =  ((uch *) cd->sector)+16+quarter*512;
-	memcpy(dest, source, 512); 
-      }
-      else {
-	error=1;
-      }
-    }
-    end_request(!error);
-  }
-}
-
-int get_multi_session_info(struct cdrom_multisession * mssp)
-{
-  if (!FIRST_TRACK) get_disc_status();
-  if (mssp) {
-    if (DISC_STATUS & cds_multi_session) { /* multi-session */
-      if (mssp->addr_format == CDROM_LBA)
-      	mssp->addr.lba = fsm2lba(&cd->disc_status[3]);
-      else {
-      	mssp->addr.msf.frame = cd->disc_status[3];
-      	mssp->addr.msf.second = cd->disc_status[4];
-      	mssp->addr.msf.minute = cd->disc_status[5];
-      }
-      mssp->xa_flag = 1;
-    } else {
-      mssp->xa_flag = 0;
-    }
-    return 1;
-  }
-  return 0;
-}
-
-/* Audio support. I've tried very hard, but the cm206 drive doesn't 
-   seem to have a get_toc (table-of-contents) function, while i'm
-   pretty sure it must read the toc upon disc insertion. Therefore
-   this function has been implemented through a binary search 
-   strategy. All track starts that happen to be found are stored in
-   cd->toc[], for future use. 
-
-   I've spent a whole day on a bug that only shows under Workman---
-   I don't get it. Tried everything, nothing works. If workman asks
-   for track# 0xaa, it'll get the wrong time back. Any other program
-   receives the correct value. I'm stymied.
-*/
-
-/* seek seeks to address lba. It does wait to arrive there. */
-void seek(int lba)
-{
-  int i;
-  uch seek_command[4]={c_seek, };
-  
-  fsm(lba, &seek_command[1]);
-  for (i=0; i<4; i++) type_0_command(seek_command[i], 0);
-  cd->dsb = wait_dsb();
-}
-
-uch bcdbin(unsigned char bcd)	/* stolen from mcd.c! */
-{
-  return (bcd >> 4)*10 + (bcd & 0xf);
-} 
-
-inline uch normalize_track(uch track) 
-{
-  if (track<1) return 1;
-  if (track>LAST_TRACK) return LAST_TRACK+1;
-  return track;
-}
-
-/* This function does a binary search for track start. It records all
- * tracks seen in the process. Input $track$ must be between 1 and
- * #-of-tracks+1 */
-int get_toc_lba(uch track)
-{
-  int max=74*60*75-150, min=0;
-  int i, lba, l, old_lba=0;
-  uch * q = cd->q;
-  uch ct;			/* current track */
-  int binary=0;
-  const skip = 3*60*75;
-
-  for (i=track; i>0; i--) if (cd->toc[i].track) {
-    min = fsm2lba(cd->toc[i].fsm);
-    break;
-  }
-  lba = min + skip;		/* 3 minutes */
-  do {
-    seek(lba); 
-    type_1_command(c_read_current_q, 10, q);
-    ct = normalize_track(q[1]);
-    if (!cd->toc[ct].track) {
-      l = q[9]-bcdbin(q[5]) + 75*(q[8]-bcdbin(q[4])-2 + 
-				  60*(q[7]-bcdbin(q[3])));
-      cd->toc[ct].track=q[1];	/* lead out still 0xaa */
-      fsm(l, cd->toc[ct].fsm);
-      cd->toc[ct].q0 = q[0];	/* contains adr and ctrl info */
-/*
-      if (ct==LAST_TRACK+1) 
-	printk("Leadout %x %x %x %x %d %d %d \n", q[1], q[3], q[4], q[5],
-	       q[7], q[8], q[9]);
-*/
-      if (ct==track) return l;
-    }
-    old_lba=lba;
-    if (binary) {
-      if (ct < track) min = lba; else max = lba;
-      lba = (min+max)/2; 
-    } else {
-      if(ct < track) lba += skip;
-      else {
-	binary=1;
-	max = lba; min = lba - skip;
-	lba = (min+max)/2;
-      }
-    }
-  } while (lba!=old_lba);
-  return lba;
-}
-
-void update_toc_entry(uch track) 
-{
-  track = normalize_track(track);
-  if (!cd->toc[track].track) get_toc_lba(track);
-}
-
-int read_toc_header(struct cdrom_tochdr * hp)
-{
-  if (!FIRST_TRACK) get_disc_status();
-  if (hp && DISC_STATUS & cds_all_audio) { /* all audio */
-    int i;
-    hp->cdth_trk0 = FIRST_TRACK;
-    hp->cdth_trk1 = LAST_TRACK;
-    cd->toc[1].track=1;		/* fill in first track position */
-    for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i];
-    update_toc_entry(LAST_TRACK+1);		/* find most entries */
-    return 1;
-  }
-  return 0;
-}  
-
-void play_from_to_msf(struct cdrom_msf* msfp)
-{
-  uch play_command[] = {c_play, 
-	   msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0,
-	   msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2, 2};
-  int i;
-  for (i=0; i<9; i++) type_0_command(play_command[i], 0);
-  for (i=0; i<3; i++) 
-    PLAY_TO.fsm[i] = play_command[i+4];
-  PLAY_TO.track = 0;		/* say no track end */
-  cd->dsb = wait_dsb();
-}  
-
-void play_from_to_track(int from, int to)
-{
-  uch play_command[8] = {c_play, };
-  int i;
-
-  if (from==0) {		/* continue paused play */
-    for (i=0; i<3; i++) { 
-      play_command[i+1] = cd->audio_status[i+2];
-      play_command[i+4] = PLAY_TO.fsm[i];
-    }
-  } else {
-    update_toc_entry(from); update_toc_entry(to+1);
-    for (i=0; i<3; i++) {
-      play_command[i+1] = cd->toc[from].fsm[i];
-      PLAY_TO.fsm[i] = play_command[i+4] = cd->toc[to+1].fsm[i];
-    }
-    PLAY_TO.track = to; 
-  }
-  for (i=0; i<7; i++) type_0_command(play_command[i],0);
-  for (i=0; i<2; i++) type_0_command(0x2, 0); /* volume */
-  cd->dsb = wait_dsb();
-}
-
-int get_current_q(struct cdrom_subchnl * qp)
-{
-  int i;
-  uch * q = cd->q;
-  if (type_1_command(c_read_current_q, 10, q)) return 0;
-/*  q[0] = bcdbin(q[0]); Don't think so! */
-  for (i=2; i<6; i++) q[i]=bcdbin(q[i]); 
-  qp->cdsc_adr = q[0] & 0xf; qp->cdsc_ctrl = q[0] >> 4;	/* from mcd.c */
-  qp->cdsc_trk = q[1];  qp->cdsc_ind = q[2];
-  if (qp->cdsc_format == CDROM_MSF) {
-    qp->cdsc_reladdr.msf.minute = q[3];
-    qp->cdsc_reladdr.msf.second = q[4];
-    qp->cdsc_reladdr.msf.frame = q[5];
-    qp->cdsc_absaddr.msf.minute = q[7];
-    qp->cdsc_absaddr.msf.second = q[8];
-    qp->cdsc_absaddr.msf.frame = q[9];
-  } else {
-    qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]);
-    qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]);
-  }
-  get_drive_status();
-  if (cd->dsb & dsb_play_in_progress) 
-    qp->cdsc_audiostatus = CDROM_AUDIO_PLAY ;
-  else if (PAUSED) 
-    qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED;
-  else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
-  return 1;
-}
-
-void get_toc_entry(struct cdrom_tocentry * ep)
-{
-  uch track = normalize_track(ep->cdte_track);
-  update_toc_entry(track);
-  if (ep->cdte_format == CDROM_MSF) {
-    ep->cdte_addr.msf.frame = cd->toc[track].fsm[0];
-    ep->cdte_addr.msf.second = cd->toc[track].fsm[1];
-    ep->cdte_addr.msf.minute = cd->toc[track].fsm[2];
-  } 
-  else ep->cdte_addr.lba = fsm2lba(cd->toc[track].fsm);
-  ep->cdte_adr = cd->toc[track].q0 & 0xf; 
-  ep->cdte_ctrl = cd->toc[track].q0 >> 4;
-  ep->cdte_datamode=0;
-}
-  
-/* Ioctl. I have made the statistics accessible through an ioctl
-   call. The constant is defined in cm206.h, it shouldn't clash with
-   the standard Linux ioctls. Multisession info is gathered at
-   run-time, this may turn out to be slow. */
-
-static int cm206_ioctl(struct inode * inode, struct file * file, 
-		       unsigned int cmd, unsigned long arg)
-{
-  switch (cmd) {
-#ifdef STATISTICS
-  case CM206CTL_GET_STAT:
-    if (arg >= NR_STATS) return -EINVAL;
-    else return cd->stats[arg];
-  case CM206CTL_GET_LAST_STAT:
-    if (arg >= NR_STATS) return -EINVAL;
-    else return cd->last_stat[arg];
-#endif    
-  case CDROMMULTISESSION: {
-    struct cdrom_multisession ms_info;
-    int st;
-    stats(ioctl_multisession);
-
-    st=verify_area(VERIFY_WRITE, (void *) arg, 
-		   sizeof(struct cdrom_multisession));
-    if (st) return (st);
-    memcpy_fromfs(&ms_info, (struct cdrom_multisession *) arg,
-		  sizeof(struct cdrom_multisession));
-    get_multi_session_info(&ms_info);
-    memcpy_tofs((struct cdrom_multisession *) arg, &ms_info, 
-		  sizeof(struct cdrom_multisession));
-    return 0;
-  }
-  case CDROMRESET:		/* If needed, it's probably too late anyway */
-    stop_read();
-    reset_cm260();
-    outw(dc_normal | dc_break | READ_AHEAD, r_data_control);
-    udelay(1000);		/* 750 musec minimum */
-    outw(dc_normal | READ_AHEAD, r_data_control);
-    cd->sector_last = -1;	/* flag no data buffered */
-    cd->adapter_last = -1;    
-    return 0;
-  }
-
-  get_drive_status();
-  if (cd->dsb & (dsb_drive_not_ready | dsb_tray_not_closed) )
-    return -EAGAIN; 
-
-  switch (cmd) {
-  case CDROMREADTOCHDR: {
-    struct cdrom_tochdr header;
-    int st;
-
-    st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(header));
-    if (st) return (st);
-    if (read_toc_header(&header)) {
-      memcpy_tofs((struct cdrom_tochdr *) arg, &header, sizeof(header));
-      return 0;
-    }
-    else return -ENODATA;
-  }
-  case CDROMREADTOCENTRY: {	
-    struct cdrom_tocentry entry;
-    int st;
-
-    st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry));
-    if (st) return (st);
-    memcpy_fromfs(&entry, (struct cdrom_tocentry *) arg, sizeof entry);
-    get_toc_entry(&entry);
-    memcpy_tofs((struct cdrom_tocentry *) arg, &entry, sizeof entry);
-    return 0;
-  }
-  case CDROMPLAYMSF: {
-    struct cdrom_msf msf;
-    int st;
-
-    st=verify_area(VERIFY_READ, (void *) arg, sizeof(msf));
-    if (st) return (st);
-    memcpy_fromfs(&msf, (struct cdrom_mdf *) arg, sizeof msf);
-    play_from_to_msf(&msf);
-    return 0;
-  }
-  case CDROMPLAYTRKIND: {
-    struct cdrom_ti track_index;
-    int st;
-
-    st=verify_area(VERIFY_READ, (void *) arg, sizeof(track_index));
-    if (st) return (st);
-    memcpy_fromfs(&track_index, (struct cdrom_ti *) arg, sizeof(track_index));
-    play_from_to_track(track_index.cdti_trk0, track_index.cdti_trk1);
-    return 0;
-  }
-  case CDROMSTOP: 
-    PAUSED=0;
-    if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1);
-    return 0;
-  case CDROMPAUSE: 
-    if (cd->dsb & dsb_play_in_progress) {
-      type_0_command(c_stop, 1);
-      type_1_command(c_audio_status, 5, cd->audio_status);
-      PAUSED=1;	/* say we're paused */
-    }
-    return 0;
-  case CDROMRESUME:
-    if (PAUSED) play_from_to_track(0,0);
-    PAUSED=0;
-    return 0;
-  case CDROMEJECT:
-    PAUSED=0;
-    if (cd->openfiles == 1) {	/* Must do an open before an eject! */
-      type_0_command(c_open_tray,1);
-      memset(cd->toc, 0, sizeof(cd->toc));
-      memset(cd->disc_status, 0, sizeof(cd->disc_status));
-      return 0;
-    }
-    else return -EBUSY;
-  case CDROMSTART:
-  case CDROMVOLCTRL:
-    return 0;
-  case CDROMSUBCHNL: {
-    struct cdrom_subchnl q;
-    int st;
-
-    st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(q));
-    if (st) return (st);
-    memcpy_fromfs(&q, (struct cdrom_subchnl *) arg, sizeof q);
-    if (get_current_q(&q)) {
-      memcpy_tofs((struct cdrom_subchnl *) arg, &q, sizeof q);
-      return 0;
-    }
-    else return -cmd;
-  }
-  case CDROM_GET_UPC: {
-    uch upc[10];
-    int st;
-
-    st=verify_area(VERIFY_WRITE, (void *) arg, 8);
-    if (st) return (st);
-    if (type_1_command(c_read_upc, 10, upc)) return -EIO;
-    memcpy_tofs((uch *) arg, &upc[1], 8);
-    return 0;
-  } 
-  default:
-    debug(("Unknown ioctl call 0x%x\n", cmd));
-    return -EINVAL;
-  }
-}     
-
-/* from lmscd.c */
-static struct file_operations cm206_fops = {
-	NULL,			/* lseek */
-	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
-	NULL,			/* readdir */
-	NULL,			/* select */
-	cm206_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	cm206_open,		/* open */
-	cm206_release,		/* release */
-	NULL,			/* fsync */
-	NULL,			/* fasync */
-	NULL,			/* media_change */
-	NULL			/* revalidate */
-};
-
-/* This routine gets called during init if thing go wrong, can be used
- * in cleanup_module as well. */
-void cleanup(int level)
-{
-  switch (level) {
-  case 4: 
-    if (unregister_blkdev(MAJOR_NR, "cm206")) {
-      printk("Can't unregister cm206\n");
-      return;
-    }
-  case 3: 
-    free_irq(cm206_irq);
-  case 2: 
-  case 1: 
-    kfree(cd);
-    release_region(cm206_base, 16);
-  default:
-  }
-}
-
-/* This function probes for the adapter card. It returns the base
-   address if it has found the adapter card. One can specify a base 
-   port to probe specifically, or 0 which means span all possible
-   bases. 
-
-   Linus says it is too dangerous to use writes for probing, so we
-   stick with pure reads for a while. Hope that 8 possible ranges,
-   check_region, 15 bits of one port and 6 of another make things
-   likely enough to accept the region on the first hit...
- */
-int probe_base_port(int base)
-{
-  int b=0x300, e=0x370;		/* this is the range of start addresses */
-  volatile int fool;
-#if 0
-  const pattern1=0x65, pattern2=0x1a;
-#endif
-
-  if (base) b=e=base;
-  for (base=b; base<=e; base += 0x10) {
-    if (check_region(base, 0x10)) continue;
-    fool = inw(base+2);		/* empty possibly uart_receive_buffer */
-    if((inw(base+6) & 0xffef) != 0x0001 || /* line_status */
-       (inw(base) & 0xad00) != 0) /* data status */
-      continue;
-#if 0				/* writes... dangerous... */
-    outw(dc_normal | pattern1, base+8); 
-    if ((inw(base) & 0x7f) != pattern1) continue;
-    outw(dc_normal | pattern2, base+8);
-    if ((inw(base) & 0x7f) != pattern2) continue;
-    outw(dc_normal | READ_AHEAD, base+8);
-#endif
-    return(base);
-  }
-  return 0;
-}
-
-#if !defined(MODULE) || defined(AUTO_PROBE_MODULE)
-/* Probe for irq# nr. If nr==0, probe for all possible irq's. */
-int probe_irq(int nr) {
-  int irqs, irq;
-  outw(dc_normal | READ_AHEAD, r_data_control);	/* disable irq-generation */
-  sti(); 
-  irqs = probe_irq_on();
-  reset_cm260();		/* causes interrupt */
-  udelay(10);			/* wait for it */
-  irq = probe_irq_off(irqs);
-  outw(dc_normal | READ_AHEAD, r_data_control);	/* services interrupt */
-  if (nr && irq!=nr && irq>0) return 0;	/* wrong interrupt happened */
-  else return irq;
-}
-#endif
-
-#ifdef MODULE
-
-static int cm206[2] = {0,0};	/* for compatible `insmod' parameter passing */
-void parse_options(void) 
-{
-  int i;
-  for (i=0; i<2; i++) {
-    if (0x300 <= cm206[i] && i<= 0x370 && cm206[i] % 0x10 == 0) {
-      cm206_base = cm206[i];
-      auto_probe=0;
-    }
-    else if (3 <= cm206[i] && cm206[i] <= 15) {
-      cm206_irq = cm206[i];
-      auto_probe=0;
-    }
-  }
-}
-
-#define cm206_init init_module
-
-#endif MODULE
-
-
-int cm206_init(void)
-{
-  uch e=0;
-  long int size=sizeof(struct cm206_struct);
-
-  printk("cm206: v" VERSION);
-#if defined(MODULE) 
-  parse_options();
-#if !defined(AUTO_PROBE_MODULE)
-   auto_probe=0;
-#endif
-#endif
-  cm206_base = probe_base_port(auto_probe ? 0 : cm206_base);
-  if (!cm206_base) {
-    printk(" can't find adapter!\n");
-    return -EIO;
-  }
-  printk(" adapter at 0x%x", cm206_base);
-  request_region(cm206_base, 16, "cm206");
-  cd = (struct cm206_struct *) kmalloc(size, GFP_KERNEL);
-  if (!cd) return -EIO;
-  /* Now we have found the adaptor card, try to reset it. As we have
-   * found out earlier, this process generates an interrupt as well,
-   * so we might just exploit that fact for irq probing! */
-#if !defined(MODULE) || defined(AUTO_PROBE_MODULE)
-  cm206_irq = probe_irq(auto_probe ? 0 : cm206_irq);	
-  if (cm206_irq<=0) {
-    printk("can't find IRQ!\n");
-    cleanup(1);
-    return -EIO;
-  }
-  else printk(" IRQ %d found\n", cm206_irq);
-#else
-  reset_cm260();
-  printk(" using IRQ %d\n", cm206_irq);
-#endif
-  if (send_receive_polled(c_drive_configuration) != c_drive_configuration) 
-    {
-      printk(" drive not there\n");
-      cleanup(1);
-      return -EIO;
-    }
-  e = send_receive_polled(c_gimme);
-  printk("Firmware revision %d", e & dcf_revision_code);
-  if (e & dcf_transfer_rate) printk(" double");
-  else printk(" single");
-  printk(" speed drive");
-  if (e & dcf_motorized_tray) printk(", motorized tray");
-  if (request_irq(cm206_irq, cm206_interrupt, 0, "cm206")) {
-    printk("\nUnable to reserve IRQ---aborted\n");
-    cleanup(2);
-    return -EIO;
-  }
-  printk(".\n");
-  if (register_blkdev(MAJOR_NR, "cm206", &cm206_fops) != 0) {
-    printk("Cannot register for major %d!\n", MAJOR_NR);
-    cleanup(3);
-    return -EIO;
-  }
-  blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-  read_ahead[MAJOR_NR] = 16;	/* reads ahead what? */
-  bh_base[CM206_BH].routine = cm206_bh;
-  enable_bh(CM206_BH);
-
-  memset(cd, 0, sizeof(*cd));	/* give'm some reasonable value */
-  cd->sector_last = -1;		/* flag no data buffered */
-  cd->adapter_last = -1;
-  cd->timer.function = cm206_timeout;
-  cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97;
-  printk("%d kB adapter memory available, "  
-	 " %ld bytes kernel memory used.\n", cd->max_sectors*2, size);
-  return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-  cleanup(4);
-  printk("cm206 removed\n");
-}
-      
-#else MODULE
-
-/* This setup function accepts either `auto' or numbers in the range
- * 3--11 (for irq) or 0x300--0x370 (for base port) or both. */
-void cm206_setup(char *s, int *p)
-{
-  int i;
-  if (!strcmp(s, "auto")) auto_probe=1;
-  for(i=1; i<=p[0]; i++) {
-    if (0x300 <= p[i] && i<= 0x370 && p[i] % 0x10 == 0) {
-      cm206_base = p[i];
-      auto_probe = 0;
-    }
-    else if (3 <= p[i] && p[i] <= 15) {
-      cm206_irq = p[i];
-      auto_probe = 0;
-    }
-  }
-}
-#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.35/linux/drivers/block/floppy.c	Fri Oct 13 14:44:32 1995
+++ linux/drivers/block/floppy.c	Sat Oct 21 19:37:35 1995
@@ -81,12 +81,21 @@
  * errors to allow safe writing by specialized programs.
  */
 
+/*
+ * 1995/8/26 -- Andreas Busse -- added Mips support.
+ */
+
 /* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
  * by defining bit 1 of the "stretch" parameter to mean put sectors on the
  * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
  * drives are "upside-down").
  */
 
+/*
+ * 1995/18/10 -- Ralf Baechle -- Portability cleanup; move machine dependend
+ * features to asm/floppy.h.
+ */
+
 #define CONFIG_FLOPPY_SANITY
 #undef  CONFIG_FLOPPY_SILENT_DCL_CLEAR
 
@@ -113,8 +122,6 @@
 int FLOPPY_IRQ=6;
 int FLOPPY_DMA=2;
 int ALLOWED_DRIVE_MASK = 0x33;
-int FDC1 = 0x3f0;
-int FDC2 = -1;
 
 #endif
 
@@ -128,8 +135,6 @@
 
 #define FLOPPY_IRQ 6
 #define FLOPPY_DMA 2
-#define FDC1 0x3f0
-static int FDC2=-1;
 #endif
 
 #define MODULE_AWARE_DRIVER
@@ -223,17 +228,19 @@
 #include <linux/ioport.h>
 
 #include <asm/dma.h>
+#include <asm/floppy.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
 
 #define MAJOR_NR FLOPPY_MAJOR
-#include "blk.h"
-
+#include <linux/blk.h>
 
-/* Dma Memory related stuff */
 
+/*
+ * Dma Memory related stuff
+ */
 /* Pure 2^n version of get_order */
 static inline int __get_order (int size)
 {
@@ -260,25 +267,9 @@
 	return __get_dma_pages(GFP_KERNEL,order);
 }
 
-/* End dma memory related stuff */
-
 static unsigned int fake_change = 0;
 static int initialising=1;
 
-/*
- * Again, the CMOS information doesn't work on the alpha..
- */
-#ifdef __alpha__
-#define FLOPPY0_TYPE 6
-#define FLOPPY1_TYPE 0
-#else
-#define FLOPPY0_TYPE	((CMOS_READ(0x10) >> 4) & 15)
-#define FLOPPY1_TYPE	(CMOS_READ(0x10) & 15)
-#endif
-
-#define N_FDC 2
-#define N_DRIVE 8
-
 static inline int TYPE(kdev_t x) {
 	return  (MINOR(x)>>2) & 0x1f;
 }
@@ -343,24 +334,9 @@
  */
 #define MAX_DISK_SIZE 2 /* 3984*/
 
-
 #define K_64	0x10000		/* 64KB */
 
 /*
- * The DMA channel used by the floppy controller cannot access data at
- * addresses >= 16MB
- *
- * Went back to the 1MB limit, as some people had problems with the floppy
- * driver otherwise. It doesn't matter much for performance anyway, as most
- * floppy accesses go through the track buffer.
- */
-#ifdef __alpha__
-# define CROSS_64KB(a,s)	(0)
-#else
-# define CROSS_64KB(a,s) ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64)
-#endif
-
-/*
  * globals used by 'result()'
  */
 #define MAX_REPLIES 10
@@ -737,13 +713,13 @@
 	if (UDP->flags & FD_DEBUG){
 		DPRINT1("checking disk change line for drive %d\n",drive);
 		DPRINT1("jiffies=%ld\n", jiffies);
-		DPRINT1("disk change line=%x\n",inb_p(FD_DIR)&0x80);
+		DPRINT1("disk change line=%x\n",fd_inb(FD_DIR)&0x80);
 		DPRINT1("flags=%x\n",UDRS->flags);
 	}
 #endif
 	if (UDP->flags & FD_BROKEN_DCL)
 		return UTESTF(FD_DISK_CHANGED);
-	if( (inb_p(FD_DIR) ^ UDP->flags) & 0x80){
+	if( (fd_inb(FD_DIR) ^ UDP->flags) & 0x80){
 		USETF(FD_VERIFY); /* verify write protection */
 		if(UDRS->maxblock){
 			/* mark it changed */
@@ -794,7 +770,7 @@
 			disk_change(drive);
 		}
 		FDCS->dor = newdor;
-		outb_p(newdor, FD_DOR);
+		fd_outb(newdor, FD_DOR);
 
 		unit = newdor & 0x3;
 		if(!is_selected(olddor, unit) && is_selected(newdor,unit)){
@@ -813,8 +789,8 @@
 {
 	if (DP->select_delay)
 		return;
-	outb_p(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
-	outb_p(FDCS->dor, FD_DOR);
+	fd_outb(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+	fd_outb(FDCS->dor, FD_DOR);
 	DRS->select_date = jiffies;
 }
 
@@ -845,7 +821,7 @@
 	set_dor(1-fdc, ~8, 0);
 	if ( FDCS->rawcmd == 2 )
 		reset_fdc_info(1);
-	if( inb_p(FD_STATUS) != STATUS_READY )
+	if ( fd_inb(FD_STATUS) != STATUS_READY )
 		FDCS->reset = 1;
 }
 
@@ -1092,14 +1068,13 @@
 	}
 #endif
 	cli();
-	disable_dma(FLOPPY_DMA);
-	clear_dma_ff(FLOPPY_DMA);
-	set_dma_mode(FLOPPY_DMA,
-		     (raw_cmd->flags & FD_RAW_READ)?
-		     DMA_MODE_READ : DMA_MODE_WRITE);
-	set_dma_addr(FLOPPY_DMA, virt_to_bus(raw_cmd->kernel_data));
-	set_dma_count(FLOPPY_DMA, raw_cmd->length);
-	enable_dma(FLOPPY_DMA);
+	fd_disable_dma();
+	fd_clear_dma_ff();
+	fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
+	                 DMA_MODE_READ : DMA_MODE_WRITE);
+	fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data));
+	fd_set_dma_count(raw_cmd->length);
+	fd_enable_dma();
 	sti();
 	floppy_disable_hlt();
 }
@@ -1114,12 +1089,12 @@
 	if (FDCS->reset)
 		return -1;
 	for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
-		rstatus = inb_p(FD_STATUS);
+		rstatus = fd_inb(FD_STATUS);
 		status =  rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA);
 		if (!(status & STATUS_READY))
 			continue;
 		if (status == STATUS_READY){
-			outb_p(byte,FD_DATA);
+			fd_outb(byte,FD_DATA);
 
 #ifdef CONFIG_FLOPPY_SANITY
 			output_log[output_log_pos].data = byte;
@@ -1147,7 +1122,7 @@
 	if (FDCS->reset)
 		return -1;
 	for (counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
-		status = inb_p(FD_STATUS)&
+		status = fd_inb(FD_STATUS)&
 			(STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
 		if (!(status & STATUS_READY))
 			continue;
@@ -1165,7 +1140,7 @@
 				DPRINT("floppy_stat reply overrun\n");
 				break;
 			}
-			reply_buffer[i++] = inb_p(FD_DATA);
+			reply_buffer[i++] = fd_inb(FD_DATA);
 		}
 	}
 	FDCS->reset = 1;
@@ -1321,7 +1296,7 @@
 		return 0;
 
 	/* Set dtr */
-	outb_p(raw_cmd->rate, FD_DCR);
+	fd_outb(raw_cmd->rate, FD_DCR);
 
 	/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
 	 * need a stabilization period of several milliseconds to be
@@ -1751,11 +1726,11 @@
 	FDCS->reset = 0;
 	reset_fdc_info(0);
 	if ( FDCS->version >= FDC_82077 )
-		outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
+		fd_outb(0x80 | ( FDCS->dtr &3), FD_STATUS);
 	else {
-		outb_p(FDCS->dor & ~0x04, FD_DOR);
+		fd_outb(FDCS->dor & ~0x04, FD_DOR);
 		udelay(FD_RESET_DELAY);
-		outb(FDCS->dor, FD_DOR);
+		fd_outb(FDCS->dor, FD_DOR);
 	}
 }
 
@@ -1794,12 +1769,12 @@
 	for(i=0; i<N_FDC; i++){
 		if(FDCS->address != -1){
 			printk("dor %d = %x\n", i, fdc_state[i].dor );
-			outb_p(fdc_state[i].address+2, fdc_state[i].dor);
+			fd_outb(fdc_state[i].address+2, fdc_state[i].dor);
 			udelay(1000); /* maybe we'll catch an interrupt... */
 		}
 	}
 #endif
-	printk("status=%x\n", inb_p(FD_STATUS));
+	printk("status=%x\n", fd_inb(FD_STATUS));
 	printk("fdc_busy=%d\n", fdc_busy);
 	if( DEVICE_INTR)
 		printk("DEVICE_INTR=%p\n", DEVICE_INTR);
@@ -1828,7 +1803,7 @@
 	sti();
 
 	floppy_enable_hlt();
-	disable_dma(FLOPPY_DMA);
+	fd_disable_dma();
 	/* avoid dma going to a random drive after shutdown */
 
 	if(!initialising)
@@ -2411,10 +2386,14 @@
 		if ( ((unsigned long)buffer) % 512 )
 			DPRINT1("%p buffer not aligned\n", buffer);
 #endif
-		if ( CT(COMMAND) == FD_READ )
+		if ( CT(COMMAND) == FD_READ ) {
+			fd_cacheflush(dma_buffer, size);
 			memcpy( buffer, dma_buffer, size);
-		else
+		}	
+		else {
 			memcpy( dma_buffer, buffer, size);
+			fd_cacheflush(dma_buffer, size);
+		}	
 		remaining -= size;
 		if ( !remaining)
 			break;
@@ -2862,6 +2841,9 @@
 	i = verify_area(VERIFY_WRITE,param,size);
 	if (i)
 		return i;
+	fd_cacheflush(address, size);		/* is this necessary ??? */
+			/* Ralf: Yes; only the l2 cache is completly chipset
+			   controlled */
 	memcpy_tofs(param,(void *) address, size);
 	return 0;
 }
@@ -3920,6 +3902,8 @@
 	initialising=0;
 	if(have_no_fdc)
 		unregister_blkdev(MAJOR_NR,"fd");
+	else
+		virtual_dma_init();
 	return have_no_fdc;
 }
 
@@ -3939,28 +3923,27 @@
 		if(FDCS->address != -1){
 			fdc = i;
 			reset_fdc_info(1);
-			outb_p(FDCS->dor, FD_DOR);
+			fd_outb(FDCS->dor, FD_DOR);
 		}
 	}
 	set_dor(0, ~0, 8);  /* avoid immediate interrupt */
 
-	if (request_irq(FLOPPY_IRQ, floppy_interrupt,
-			SA_INTERRUPT|SA_SAMPLE_RANDOM, "floppy")) {
+	if (fd_request_irq()) {
 		DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
 			FLOPPY_IRQ);
 		return -1;
 	}
-	if (request_dma(FLOPPY_DMA,"floppy")) {
+	if (fd_request_dma()) {
 		DPRINT1("Unable to grab DMA%d for the floppy driver\n",
 			FLOPPY_DMA);
-		free_irq(FLOPPY_IRQ);
+		fd_free_irq();
 		return -1;
 	}
 	for(fdc = 0; fdc < N_FDC ; fdc++)
 		if(FDCS->address != -1)
-			outb_p(FDCS->dor, FD_DOR);
+			fd_outb(FDCS->dor, FD_DOR);
 	fdc = 0;
-	enable_irq(FLOPPY_IRQ);
+	fd_enable_irq();
 	return 0;
 }
 
@@ -3981,10 +3964,10 @@
 #ifdef FD_MODULE
 	MOD_DEC_USE_COUNT;
 #endif
-	disable_dma(FLOPPY_DMA);
-	free_dma(FLOPPY_DMA);
-	disable_irq(FLOPPY_IRQ);
-	free_irq(FLOPPY_IRQ);
+	fd_disable_dma();
+	fd_free_dma();
+	fd_disable_irq();
+	fd_free_irq();
 
 	set_dor(0, ~0, 8);
 #if N_FDC > 1
diff -u --recursive --new-file v1.3.35/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v1.3.35/linux/drivers/block/genhd.c	Tue Oct 10 18:46:31 1995
+++ linux/drivers/block/genhd.c	Fri Oct 20 10:10:39 1995
@@ -213,7 +213,8 @@
 	if (!tested_for_dm6++) {	/* only check for DM6 *once* */
 		extern int ide_xlate_1024(kdev_t, int, const char *);
 		/* check for various "disk managers" which do strange things */
-		if (p->sys_ind == EZD_PARTITION) {
+		int ezstring = !strncmp(data+0x1a3, "Micro House", 11);
+		if (p->sys_ind == EZD_PARTITION || ezstring) {
 			/*
 			 * The remainder of the disk must be accessed using
 			 * a translated geometry that reduces the number of 
diff -u --recursive --new-file v1.3.35/linux/drivers/block/gscd.c linux/drivers/block/gscd.c
--- v1.3.35/linux/drivers/block/gscd.c	Tue Oct 10 18:46:31 1995
+++ linux/drivers/block/gscd.c	Thu Jan  1 02:00:00 1970
@@ -1,1126 +0,0 @@
-#define GSCD_VERSION "0.4a Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>"
-
-/*
-	linux/drivers/block/gscd.c - GoldStar R420 CDROM driver
-
-        Copyright (C) 1995  Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
-        based upon pre-works by   Eberhard Moenkeberg <emoenke@gwdg.de>
-        
-
-        For all kind of other information about the GoldStar CDROM
-        and this Linux device driver I installed a WWW-URL:
-        http://linux.rz.fh-hannover.de/~raupach        
-
-
-             If you are the editor of a Linux CD, you should
-             enable gscd.c within your boot floppy kernel and
-             send me one of your CDs for free.
-
-
-        --------------------------------------------------------------------
-	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; if not, write to the Free Software
-	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/* These settings are for various debug-level. Leave they untouched ... */ 
-#define  NO_GSCD_DEBUG 
-#define  NO_IOCTL_DEBUG
-#define  NO_MODULE_DEBUG
-#define  NO_FUTURE_WORK
-/*------------------------*/
-
-#include <linux/config.h>
-
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/malloc.h>
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[] = UTS_RELEASE;
-#endif
-#endif MODULE
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/major.h>
-#include <linux/string.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#define MAJOR_NR GOLDSTAR_CDROM_MAJOR
-#include "blk.h"
-#define gscd_port gscd /* for compatible parameter passing with "insmod" */
-#include <linux/gscd.h>
-
-
-static int gscdPresent            = 0;
-
-static unsigned char gscd_buf[2048];    /* buffer for block size conversion */
-static int   gscd_bn              = -1;
-static short gscd_port            = GSCD_BASE_ADDR;
-
-/* Kommt spaeter vielleicht noch mal dran ...
- *    static struct wait_queue *gscd_waitq = NULL;
- */ 
-
-static void gscd_transfer         (void);
-static void gscd_read_cmd         (void);
-static void gscd_hsg2msf          (long hsg, struct msf *msf);
-static void gscd_bin2bcd          (unsigned char *p);
-
-/* Schnittstellen zum Kern/FS */
-
-static void do_gscd_request       (void);
-static int  gscd_ioctl            (struct inode *, struct file *, unsigned int, unsigned long);
-static int  gscd_open             (struct inode *, struct file *);
-static void gscd_release          (struct inode *, struct file *);
-static int  check_gscd_med_chg    (kdev_t);
-
-/*      GoldStar Funktionen    */
-
-static void cc_Reset             (void);
-static int  wait_drv_ready       (void);
-static int  find_drives          (void);
-static void cmd_out              (int, char *, char *, int);
-static void cmd_status           (void);
-static void cc_Ident             (char *);
-static void cc_SetSpeed          (void);
-static void init_cd_drive        (int);
-
-static int  get_status           (void);
-static void clear_Audio          (void);
-static void cc_invalidate        (void);
-
-/* some things for the next version */
-#ifdef FUTURE_WORK
-static void update_state          (void);
-static long gscd_msf2hsg          (struct msf *mp);
-static int  gscd_bcd2bin          (unsigned char bcd);
-#endif
-
-/*    common GoldStar Initialization    */
-
-static int my_gscd_init (void);
-
-
-/*      lo-level cmd-Funktionen    */
-
-static void cmd_info_in          ( char *, int );
-static void cmd_end              ( void );
-static void cmd_read_b           ( char *, int, int );
-static void cmd_read_w           ( char *, int, int );
-static int  cmd_unit_alive       ( void );
-static void cmd_write_cmd        ( char * );
-
-
-/*      GoldStar Variablen     */
-
-static int  curr_drv_state;
-static int  drv_states[]       = {0,0,0,0,0,0,0,0};
-static int  drv_mode;
-static int  disk_state;
-static int  speed;
-static int  ndrives;
-
-static unsigned char drv_num_read;
-static unsigned char f_dsk_valid;
-static unsigned char current_drive;
-static unsigned char f_drv_ok;
-
-
-static char f_AudioPlay;
-static char f_AudioPause;
-static int  AudioStart_m;
-static int  AudioStart_f;
-static int  AudioEnd_m;
-static int  AudioEnd_f;
- 
-
-static struct file_operations gscd_fops = {
-	NULL,			/* lseek - default */
-	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
-	NULL,			/* readdir - bad */
-	NULL,			/* select */
-	gscd_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	gscd_open,		/* open */
-	gscd_release,		/* release */
-	NULL,                   /* fsync */
-	NULL,                   /* fasync*/
-	check_gscd_med_chg,     /* media change */
-	NULL                    /* revalidate */
-};
-
-/* 
- * Checking if the media has been changed
- * (not yet implemented)
- */
-static int check_gscd_med_chg (kdev_t full_dev)
-{
-   int target;
-
-
-   target = MINOR(full_dev);
-
-   if (target > 0) 
-   {
-      printk("GSCD: GoldStar CD-ROM request error: invalid device.\n");
-      return 0;
-   }
-
-   #ifdef GSCD_DEBUG
-   printk ("gscd: check_med_change\n");
-   #endif
- 
-  return 0;
-}
-
-
-void gscd_setup (char *str, int *ints)
-{
-  if (ints[0] > 0) 
-  {
-     gscd_port = ints[1];
-  }
-}
-
-
-static int gscd_ioctl (struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
-{
-unsigned char to_do[10];
-unsigned char dummy;
-
-                             
-    switch (cmd)
-    {
-       case CDROMSTART:     /* Spin up the drive */
-		/* Don't think we can do this.  Even if we could,
- 		 * I think the drive times out and stops after a while
-		 * anyway.  For now, ignore it.
-		 */
-            return 0;
-
-       case CDROMRESUME:   /* keine Ahnung was das ist */
-            return 0;
-
-
-       case CDROMEJECT:
-            cmd_status ();
-            to_do[0] = CMD_TRAY_CTL;
-            cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
-
-            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.
- */
-
-static void gscd_transfer (void)
-{
-long offs;
-
-	while (CURRENT -> nr_sectors > 0 && gscd_bn == CURRENT -> sector / 4)
-	{
-		offs = (CURRENT -> sector & 3) * 512;
-		memcpy(CURRENT -> buffer, gscd_buf + offs, 512);
-		CURRENT -> nr_sectors--;
-		CURRENT -> sector++;
-		CURRENT -> buffer += 512;
-	}
-}
-
-
-/*
- * I/O request routine called from Linux kernel.
- */
-
-static void do_gscd_request (void)
-{
-unsigned int block,dev;
-unsigned int nsect;
-
-repeat:
-	if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) return;
-	INIT_REQUEST;
-	dev = MINOR(CURRENT->rq_dev);
-	block = CURRENT->sector;
-	nsect = CURRENT->nr_sectors;
-
-	if (CURRENT == NULL || CURRENT -> sector == -1)
-		return;
-
-	if (CURRENT -> cmd != READ)
-	{
-		printk("GSCD: bad cmd %d\n", CURRENT -> cmd);
-		end_request(0);
-		goto repeat;
-	}
-
-	if (MINOR(CURRENT -> rq_dev) != 0)
-	{
-		printk("GSCD: this version supports only one device\n");
-		end_request(0);
-		goto repeat;
-	}
-
-	gscd_transfer();
-
-	/* if we satisfied the request from the buffer, we're done. */
-
-	if (CURRENT -> nr_sectors == 0)
-	{
-		end_request(1);
-		goto repeat;
-	}
-
-#ifdef GSCD_DEBUG
-        printk ("GSCD: dev %d, block %d, nsect %d\n", dev, block, nsect );
-#endif
-
-	gscd_read_cmd ();
-}
-
-
-
-/*
- * Check the result of the set-mode command.  On success, send the
- * read-data command.
- */
-
-static void
-gscd_read_cmd (void)
-{
-long   block;
-struct gscd_Play_msf gscdcmd;
-char   cmd[] = { CMD_READ, 0x80, 0,0,0, 0,1 }; /* cmd mode M-S-F secth sectl */
-
-
-
-        cmd_status ();
-        if ( disk_state & (ST_NO_DISK | ST_DOOR_OPEN) )
-        {
-           printk ( "GSCD: no disk or door open\n" );
-           end_request (0);
-        }
-        else
-        {
-           if ( disk_state & ST_INVALID )
-           {
-              printk ( "GSCD: disk invalid\n" );
-              end_request (0);
-           }
-           else
-           {
-              gscd_bn = -1;		/* purge our buffer */
-              block = CURRENT -> sector / 4;
-              gscd_hsg2msf(block, &gscdcmd.start);	/* cvt to msf format */
-
-              cmd[2] = gscdcmd.start.min;
-              cmd[3] = gscdcmd.start.sec;
-              cmd[4] = gscdcmd.start.frame;
-
-#ifdef GSCD_DEBUG
-              printk ("GSCD: read msf %d:%d:%d\n", cmd[2], cmd[3], cmd[4] ); 
-#endif 
-              cmd_out ( TYPE_DATA, (char *)&cmd, (char *)&gscd_buf[0], 1 );
-        
-              gscd_bn = CURRENT -> sector / 4;
-              gscd_transfer();
-              end_request(1);
-	   }
-	}
-	SET_TIMER(do_gscd_request, 1);
-}
-
-
-/*
- * Open the device special file.  Check that a disk is in.
- */
-
-static int gscd_open (struct inode *ip, struct file *fp)
-{
-int   st;
-
-#ifdef GSCD_DEBUG
-printk ( "GSCD: open\n" );
-#endif
-
-	if (gscdPresent == 0)
-		return -ENXIO;			/* no hardware */
-
-        get_status ();
-        st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN);
-        if ( st )
-        {
-           printk ( "GSCD: no disk or door open\n" );
-           return -ENXIO;
-        }
-                   
-/*	if (updateToc() < 0)
-		return -EIO;
-*/
-
-        #ifdef MODULE
-           MOD_INC_USE_COUNT;
-        #endif
- 
-	return 0;
-}
-
-
-/*
- * On close, we flush all gscd blocks from the buffer cache.
- */
-
-static void gscd_release (struct inode * inode, struct file * file)
-{
-
-#ifdef GSCD_DEBUG
-printk ( "GSCD: release\n" );
-#endif
-
-	gscd_bn = -1;
-	sync_dev(inode->i_rdev);
-	invalidate_buffers(inode -> i_rdev);
-
-        #ifdef MODULE
-           MOD_DEC_USE_COUNT;
-        #endif
-}
-
-
-int get_status (void)
-{
-int  status;
-
-    cmd_status ();
-    status = disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01);
-     
-    if ( status == (ST_x08 | ST_x04 | ST_INVALID | ST_x01) )
-    {
-       cc_invalidate ();
-       return 1;
-    }
-    else
-    {
-       return 0;
-    }
-}
-
-
-void cc_invalidate (void)
-{
-   drv_num_read  = 0xFF;
-   f_dsk_valid   = 0xFF;
-   current_drive = 0xFF;
-   f_drv_ok      = 0xFF;
-
-   clear_Audio ();
-   
-}   
-
-void clear_Audio (void)
-{
-
-   f_AudioPlay = 0;
-   f_AudioPause = 0;
-   AudioStart_m = 0;
-   AudioStart_f = 0;
-   AudioEnd_m   = 0;
-   AudioEnd_f   = 0;
-   
-}
-
-/*
- *   waiting ?  
- */
-
-int wait_drv_ready (void)
-{
-int found, read;
-
-   do
-   {  
-       found = inb ( GSCDPORT(0) ); 
-       found &= 0x0f;
-       read  = inb ( GSCDPORT(0) );
-       read  &= 0x0f;
-   } while ( read != found );
-   
-#ifdef GSCD_DEBUG
-printk ( "Wait for: %d\n", read );
-#endif   
-   
-   return read;
-}
-
-void cc_Ident (char * respons)
-{
-char to_do [] = {CMD_IDENT, 0, 0};
-
-    cmd_out (TYPE_INFO, (char *)&to_do, (char *)respons, (int)0x1E );
-
-}
-
-void cc_SetSpeed (void)
-{
-char to_do [] = {CMD_SETSPEED, 0, 0};
-char dummy;
-
-    if ( speed > 0 )
-    {
-       to_do[1] = speed & 0x0F;
-       cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
-    }
-}
-
-
-void cc_Reset (void)
-{
-char to_do [] = {CMD_RESET, 0};
-char dummy;
-
-   cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
-}
-
-
-
-void cmd_status (void)
-{
-char to_do [] = {CMD_STATUS, 0};
-char dummy;
-
-   cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
-
-#ifdef GSCD_DEBUG
-printk ("GSCD: Status: %d\n", disk_state );
-#endif
-
-}
-
-void cmd_out ( int cmd_type, char * cmd, char * respo_buf, int respo_count )
-{
-int        result;
-
-
-       result = wait_drv_ready ();
-       if ( result != drv_mode )
-       {
-       unsigned long test_loops = 0xFFFF;
-       int           i,dummy;
-
-          outb ( curr_drv_state, GSCDPORT(0));
-          
-          /* LOCLOOP_170 */
-          do
-          {
-             result = wait_drv_ready ();
-             test_loops--;
-          } while ( (result != drv_mode) && (test_loops > 0) );
-
-          if ( result != drv_mode )
-          {
-             disk_state = ST_x08 | ST_x04 | ST_INVALID;
-             return;
-          }          
-        
-          /* ...and waiting */
-          for ( i=1,dummy=1 ; i<0xFFFF ; i++ )
-          {
-             dummy *= i;
-          }              
-       }
-
-       /* LOC_172 */    
-       /* check the unit */
-       /* and wake it up */
-       if ( cmd_unit_alive () != 0x08 )
-       {
-          /* LOC_174 */
-          /* game over for this unit */
-          disk_state = ST_x08 | ST_x04 | ST_INVALID;
-          return;
-       }
-       
-       /* LOC_176 */
-       #ifdef GSCD_DEBUG
-        printk ("LOC_176 ");
-       #endif       
-       if ( drv_mode == 0x09 )
-       {
-          /* magic... */
-          printk ("GSCD: magic ...\n");       
-          outb ( result, GSCDPORT(2));
-       }
-
-       /* write the command to the drive */
-       cmd_write_cmd (cmd);
-
-       /* LOC_178 */
-       for (;;)
-       {
-          result = wait_drv_ready ();
-          if ( result != drv_mode )
-          {
-             /* LOC_179 */
-             if ( result == 0x04 )                  /* Mode 4 */
-             {
-                /* LOC_205 */
-                #ifdef GSCD_DEBUG
-                printk ("LOC_205 ");	              
-                #endif
-                disk_state = inb ( GSCDPORT (2));
-             
-                do
-                {
-                   result = wait_drv_ready ();
-                } while ( result != drv_mode );
-                return;
-
-             }
-             else
-             {
-                if ( result == 0x06 )               /* Mode 6 */
-                {
-                   /* LOC_181 */
-                   #ifdef GSCD_DEBUG
-                    printk ("LOC_181 ");	              
-                   #endif
-
-                   if (cmd_type == TYPE_DATA)
-                   {
-                      /* read data */
-                      /* LOC_184 */
-                      if ( drv_mode == 9 )
-                      {
-                         /* read the data to the buffer (word) */
- 
-                         /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */
-                         cmd_read_w ( respo_buf, respo_count, CD_FRAMESIZE/2 );
-                         return;
-                      } 
-                      else
-                      { 
-                         /* read the data to the buffer (byte) */
-                         
-                         /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW)    */
-                         cmd_read_b ( respo_buf, respo_count, CD_FRAMESIZE );
-                         return;
-                      }
-                   }
-                   else
-                   { 
-                      /* read the info to the buffer */
-                      cmd_info_in ( respo_buf, respo_count ); 
-                      return;
-                   }                  
-
-                   return;
-                }
-             }
-
-          }
-          else
-          {
-             disk_state = ST_x08 | ST_x04 | ST_INVALID;         
-             return;
-          }    
-       } /* for (;;) */
-
-
-#ifdef GSCD_DEBUG
-printk ("\n");       
-#endif    
-}
-
-
-static void cmd_write_cmd ( char *pstr )
-{
-int  i,j;
-
-    /* LOC_177 */
-    #ifdef GSCD_DEBUG
-     printk ("LOC_177 ");       
-    #endif
-       
-    /* calculate the number of parameter */
-    j = *pstr & 0x0F;
-
-    /* shift it out */
-    for ( i=0 ; i<j ; i++ )
-    {
-       outb ( *pstr, GSCDPORT(2) );
-       pstr++;
-    }
-}
-
-
-static int cmd_unit_alive ( void )
-{
-int            result;
-unsigned long  max_test_loops;
-
-
-    /* LOC_172 */
-    #ifdef GSCD_DEBUG
-     printk ("LOC_172 ");       
-    #endif
-
-    outb ( curr_drv_state, GSCDPORT(0));       
-    max_test_loops = 0xFFFF;
-     
-    do
-    {
-       result = wait_drv_ready ();
-       max_test_loops--;
-    } while ( (result != 0x08) && (max_test_loops > 0) );
-
-    return result;
-}       
-
-
-static void cmd_info_in ( char *pb, int count )
-{
-int   result;
-char  read;
-
-
-        /* read info */
-        /* LOC_182 */
-        #ifdef GSCD_DEBUG
-         printk ("LOC_182 ");				              
-        #endif
-       
-        do
-        {
-           read = inb (GSCDPORT(2)); 
-           if ( count > 0 )
-           {
-              *pb = read;
-              pb++;
-              count--;
-           }
-                         
-           /* LOC_183 */
-           do
-           {
-              result = wait_drv_ready ();
-           } while ( result == 0x0E );
-        } while ( result == 6 );                     
-
-        cmd_end ();
-        return;
-}
-
-
-static void cmd_read_b ( char *pb, int count, int size )
-{
-int  result;
-int  i;
-                     
-                     
-       /* LOC_188 */
-       /* LOC_189 */
-       #ifdef GSCD_DEBUG
-        printk ("LOC_189 ");			              
-       #endif
-
-       do 
-       {
-          do
-          {
-             result = wait_drv_ready ();
-          } while ( result != 6 || result == 0x0E );
-                         
-          if ( result != 6 )
-          {
-             cmd_end ();
-             return;
-          }                       
-          
-          #ifdef GSCD_DEBUG
-           printk ("LOC_191 ");       
-          #endif
-
-          for ( i=0 ; i< size ; i++ )
-          {
-             *pb = inb (GSCDPORT(2));
-             pb++;
-          }
-          count--;
-       } while ( count > 0 );
-       
-       cmd_end ();
-       return;
-}
-
-
-static void cmd_end (void)
-{
-int   result;
-
-
-      /* LOC_204 */
-      #ifdef GSCD_DEBUG
-       printk ("LOC_204 ");				              
-      #endif
-      
-      do
-      {
-         result = wait_drv_ready ();
-         if ( result == drv_mode )
-         {
-            return;
-         }
-      } while ( result != 4 );
-
-      /* LOC_205 */
-      #ifdef GSCD_DEBUG
-       printk ("LOC_205 ");			              
-      #endif
-       
-      disk_state = inb ( GSCDPORT (2));
-             
-      do
-      {
-         result = wait_drv_ready ();
-      } while ( result != drv_mode );
-      return;
-
-}
-
-
-static void cmd_read_w ( char *pb, int count, int size )
-{
-int  result;
-int  i;
-
-
-    #ifdef GSCD_DEBUG
-     printk ("LOC_185 ");			              
-    #endif
- 
-    do 
-    {
-       /* LOC_185 */
-       do
-       {
-          result = wait_drv_ready ();
-       } while ( result != 6 || result == 0x0E );
-                         
-       if ( result != 6 )
-       {
-          cmd_end ();
-          return;
-       }                       
-                         
-       for ( i=0 ; i<size ; i++ )
-       {
-           /* na, hier muss ich noch mal drueber nachdenken */
-           *pb = inw(GSCDPORT(2));
-           pb++;
-        }
-        count--;
-     } while ( count > 0 );
-                    
-     cmd_end ();
-     return;
-}
-
-int find_drives (void)
-{
-int *pdrv;
-int drvnum;
-int subdrv;
-int i;
-
-    speed           = 0;
-    pdrv            = (int *)&drv_states;
-    curr_drv_state  = 0xFE;
-    subdrv          = 0;
-    drvnum          = 0;
-
-    for ( i=0 ; i<8 ; i++ )
-    { 
-       subdrv++;
-       cmd_status ();
-       disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01;
-       if ( disk_state != (ST_x08 | ST_x04 | ST_INVALID) )
-       {
-          /* LOC_240 */
-          *pdrv = curr_drv_state;
-          init_cd_drive (drvnum);
-          pdrv++;
-          drvnum++;
-       }
-       else
-       {
-          if ( subdrv < 2 )
-          {
-             continue;
-          }
-          else
-          {
-             subdrv = 0;
-          }
-       }   
-
-/*       curr_drv_state<<1;         <-- das geht irgendwie nicht */ 
-/* muss heissen:    curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */
-         curr_drv_state *= 2;
-         curr_drv_state |= 1;
-#ifdef GSCD_DEBUG
-         printk ("DriveState: %d\n", curr_drv_state );
-#endif
-    } 
-
-    ndrives = drvnum;
-    return drvnum;
-}    
-
-void init_cd_drive ( int num )
-{
-char resp [50];
-int  i;
-
-   printk ("GSCD: init unit %d\n", num );
-   cc_Ident ((char *)&resp);
-
-   printk ("GSCD: identification: ");
-   for ( i=0 ; i<0x1E; i++ )
-   {
-      printk ( "%c", resp[i] );
-   }
-   printk ("\n");
-   
-   cc_SetSpeed ();  
-
-}    
-
-#ifdef FUTURE_WORK
-/* return_done */
-static void update_state ( void )
-{
-unsigned int AX;
-
-
-    if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 )
-    {
-       if ( disk_state == (ST_x08 | ST_x04 | ST_INVALID))
-       {
-          AX = ST_INVALID;
-       }
-  
-       if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 )
-       {
-          invalidate ();
-          f_drv_ok = 0;
-       }
-       
-       AX |= 0x8000;
-    }
-    
-    if ( disk_state & ST_PLAYING )
-    {
-       AX |= 0x200;
-    }
-    
-    AX |= 0x100;
-    /* pkt_esbx = AX; */
-
-    disk_state = 0;
-    
-}
-#endif
-
-/* Init for the Module-Version */
-int init_module (void)
-{
-long err;
-
-
-     /* call the GoldStar-init */
-     err = my_gscd_init ( );
-
-     if ( err < 0 )
-     {
-         return err;
-     }
-     else
-     {
-        printk ( "Happy GoldStar !\n" );
-        return 0;
-     }    
-}
-
-#ifdef MODULE
-void cleanup_module (void)
-{
-
-   if (MOD_IN_USE)
-   {
-      printk("GoldStar-module in use - can't remove it.\n" );
-      return;
-   }
- 
-   if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL))
-   {
-      printk("What's that: can't unregister GoldStar-module\n" );
-      return;
-   }
-
-   release_region (gscd_port,4);
-   printk( "GoldStar-module released.\n" );
-}
-#endif
-
-
-/* Test for presence of drive and initialize it.  Called only at boot time. */
-int gscd_init (void)
-{
-   return my_gscd_init ();
-}
-
-
-/* This is the common initalisation for the GoldStar drive. */
-/* It is called at boot time AND for module init.           */
-int my_gscd_init (void)
-{
-int i;
-int result;
-
-        printk ("GSCD: version %s\n", GSCD_VERSION);
-        printk ("GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", gscd_port);
-
-        if (check_region(gscd_port, 4)) 
-        {
-	  printk("GSCD: Init failed, I/O port (%X) already in use.\n", gscd_port);
-	  return -EIO;
-	}
-	  
-
-	/* check for card */
-	result = wait_drv_ready ();
-        if ( result == 0x09 )
-        {
-           printk ("GSCD: DMA kann ich noch nicht!\n" );
-           return -EIO;
-        }
- 
-        if ( result == 0x0b )
-        {
-           drv_mode = result;
-           i = find_drives ();
-           if ( i == 0 )
-           {
-              printk ( "GSCD: GoldStar CD-ROM Drive is not found.\n" );
-              return -EIO;
-           }
-        }
-
-        if ( (result != 0x0b) && (result != 0x09) )
-        {
-              printk ("GSCD: GoldStar Interface Adapter does not exist or H/W error\n" );
-              return -EIO;
-        }          		            
-
-        /* reset all drives */
-        i = 0;
-        while ( drv_states[i] != 0 )
-        {
-           curr_drv_state = drv_states[i];
-           printk ( "GSCD: Reset unit %d ... ",i );
-           cc_Reset ();
-           printk ( "done\n" );
-           i++;
-        }
-
-	if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0)
-	{
-		printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n",
-		       MAJOR_NR);
-		return -EIO;
-	}
-
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = 4;
-        
-        disk_state = 0;
-        gscdPresent = 1;
-
-	request_region(gscd_port, 4, "gscd");
-
-        printk ( "GSCD: GoldStar CD-ROM Drive found.\n" );
-	return 0;
-}
-
-static void gscd_hsg2msf (long hsg, struct msf *msf)
-{
-	hsg += CD_BLOCK_OFFSET;
-	msf -> min = hsg / (CD_FRAMES*CD_SECS);
-	hsg %= CD_FRAMES*CD_SECS;
-	msf -> sec = hsg / CD_FRAMES;
-	msf -> frame = hsg % CD_FRAMES;
-
-	gscd_bin2bcd(&msf -> min);		/* convert to BCD */
-	gscd_bin2bcd(&msf -> sec);
-	gscd_bin2bcd(&msf -> frame);
-}
- 
-
-static void gscd_bin2bcd (unsigned char *p)
-{
-int u, t;
-
-	u = *p % 10;
-	t = *p / 10;
-	*p = u | (t << 4);
-}
-
-
-#ifdef FUTURE_WOTK
-static long gscd_msf2hsg (struct msf *mp)
-{
-	return gscd_bcd2bin(mp -> frame)
-		+ gscd_bcd2bin(mp -> sec) * CD_FRAMES
-		+ gscd_bcd2bin(mp -> min) * CD_FRAMES * CD_SECS
-		- CD_BLOCK_OFFSET;
-}
-
-static int gscd_bcd2bin (unsigned char bcd)
-{
-	return (bcd >> 4) * 10 + (bcd & 0xF);
-}
-#endif
-
-
diff -u --recursive --new-file v1.3.35/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v1.3.35/linux/drivers/block/hd.c	Tue Oct 10 18:46:31 1995
+++ linux/drivers/block/hd.c	Sat Oct 21 19:37:35 1995
@@ -42,7 +42,7 @@
 #include <asm/segment.h>
 
 #define MAJOR_NR HD_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
 
 static int revalidate_hddisk(kdev_t, int);
 
diff -u --recursive --new-file v1.3.35/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.35/linux/drivers/block/ide.c	Mon Oct 16 18:38:22 1995
+++ linux/drivers/block/ide.c	Fri Oct 20 10:10:39 1995
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.15  Oct 13, 1995
+ *  linux/drivers/block/ide.c	Version 5.16  Oct 19, 1995
  *
  *  Copyright (C) 1994, 1995  Linus Torvalds & authors (see below)
  */
@@ -163,11 +163,16 @@
  *			don't enable 2nd CMD640 PCI port during init - conflict
  *  Version 5.15	bug fix in init_cmd640_vlb()
  *			bug fix in interrupt sharing code
+ *  Version 5.16	ugh.. fix "serialize" support, broken in 5.15
+ *			remove "Huh?" from cmd640 code
+ *			added qd6580 interface speed select from Colten Edwards
  *
  *  Driver compile-time options are in ide.h
  *
  *  To do, in likely order of completion:
- *	- add ioctls to get/set interface timings on cmd640, ht6560b, triton
+ *	- add ALI M1443/1445 chipset support from derekn@vw.ece.cmu.edu
+ *	- add Promise Caching controller support from peterd@pnd-pc.demon.co.uk
+ *	- add ioctls to get/set interface timings on various interfaces
  *	- modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
  *	- improved CMD support:  handed this off to someone else
  *	- find someone to work on IDE *tape drive* support
@@ -209,7 +214,7 @@
 
 static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 0x168};
 static const byte	default_irqs[MAX_HWIFS]     = {14, 15, 11, 10};
-static int		single_threaded = 0;	/* "serialize" option */
+static int		serialized = 0;		/* "serialize" option */
 static int		disallow_unmask = 0;	/* for buggy hardware */
 
 #if (DISK_RECOVERY_TIME > 0)
@@ -2360,6 +2365,9 @@
 
 static void init_dtc2278 (void)
 {
+	unsigned long flags;
+
+	save_flags(flags);
 	cli();
 #if SET_DTC2278_MODE4
 	/*
@@ -2375,11 +2383,39 @@
 	inb(0x3f6);
 	outb_p(0x20,0xb4);
 	inb(0x3f6);
-
-	sti();
+	restore_flags(flags);
 }
 #endif /* SUPPORT_DTC2278 */
 
+#ifdef SUPPORT_QD6580
+/*
+ * QDI QD6580 EIDE controller fast support by Colten Edwards.
+ * no net access but I can be reached at pje120@cs.usask.ca
+ *
+ * I suppose that a IOCTL could be used for this and other
+ * cards like it to modify the speed using hdparm.  Someday..
+ */
+static void init_qd6580 (void)
+{
+	unsigned long flags;
+
+	/* looks like   0x4f is fast
+	 *              0x3f is medium
+	 *              0x2f is slower
+	 *              0x1f is slower yet
+	 *              ports are 0xb0 0xb2 and 0xb3
+	 */
+
+	save_flags(flags);
+	cli();
+	outb_p(0x8d,0xb0);
+	outb_p(0x0 ,0xb2);
+	outb_p(0x4f,0xb3);	/* select "fast" 0x4f */
+	inb(0x3f6);
+	restore_flags(flags);
+}
+#endif /* SUPPORT_QD6580 */
+
 #if SUPPORT_CMD640
 /*
  * ??? fixme: 
@@ -2412,7 +2448,7 @@
 	byte reg;
 	unsigned short port = 0x178;
 
-	single_threaded = 1;
+	serialized = 1;
  	printk("ide: buggy CMD640 interface: serialized, ");
 	reg = read_cmd640_vlb(port, 0x50);
 	if (reg == 0xff || (reg & 0x90) != 0x90) {
@@ -2523,7 +2559,12 @@
  * "idex=dtc2278"	: enables use of DTC2278 secondary i/f
  * "idex=ht6560b"	: enables use of HT6560B secondary i/f
  * "idex=cmd640_vlb"	: required for VLB cards with the CMD640 chip
- *			  (PCI version will be automatically detected)
+ *			  (not for PCI -- automatically detected)
+ *
+ * This option is valid ONLY on ide0, and the defaults for the base,ctl ports
+ * must not be altered.
+ *
+ * "ide0=qd6580"	: select "fast" interface speed on a qd6580 interface
  */
 void ide_setup (char *s)
 {
@@ -2561,7 +2602,7 @@
 				hwif->noprobe = 0;
 				goto done;
 			case -4: /* "serialize" */
-				printk(" -- USE ""ide%c=serialize"" INSTEAD", '0'+hw);
+				printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw);
 				goto do_serialize;
 			case 3: /* cyl,head,sect */
 				drive->media	= disk;
@@ -2579,11 +2620,17 @@
 	 * Look for interface options:  "idex="
 	 */
 	if (s[0] == 'i' && s[1] == 'd' && s[2] == 'e' && s[3] >= '0' && s[3] <= max_hwif) {
-		const char *ide_words[] = {"noprobe", "serialize", "dtc2278", "ht6560b", "cmd640_vlb", NULL};
+		const char *ide_words[] = {"noprobe", "serialize", "dtc2278", "ht6560b", "cmd640_vlb", "qd6580", NULL};
 		hw = s[3] - '0';
 		hwif = &ide_hwifs[hw];
 
 		switch (match_parm(&s[4], ide_words, vals, 3)) {
+#if SUPPORT_QD6580
+			case -6: /* "qd6580" */
+				if (hw != 0) goto bad_hwif;
+				init_qd6580();
+				goto done;
+#endif /* SUPPORT_QD6580 */
 #if SUPPORT_CMD640
 			case -5: /* "cmd640_vlb" */
 				if (hw > 1) goto bad_hwif;
@@ -2618,7 +2665,7 @@
 			case -2: /* "serialize" */
 			do_serialize:
 				if (hw > 1) goto bad_hwif;
-				single_threaded = 1;
+				serialized = 1;
 				goto done;
 			case -1: /* "noprobe" */
 				hwif->noprobe = 1;
@@ -2747,26 +2794,34 @@
 static int init_irq (ide_hwif_t *hwif)
 {
 	unsigned long flags;
-	ide_hwgroup_t *hwgroup;
+	int irq = hwif->irq;
+	ide_hwgroup_t *hwgroup = irq_to_hwgroup[irq];
 
-	/*
-	 * First, we try to grab the irq
-	 */
 	save_flags(flags);
 	cli();
-	if ((hwgroup = irq_to_hwgroup[hwif->irq]) == NULL) {
-		if (request_irq(hwif->irq, ide_intr,
-				SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name)) {
+
+	/*
+	 * Grab the irq if we don't already have it from a previous hwif
+	 */
+	if (hwgroup == NULL)  {
+		if (request_irq(irq, ide_intr, SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name)) {
 			restore_flags(flags);
 			printk(" -- FAILED!");
 			return 1;
 		}
-
-		/*
-		 * Got the irq,  now set everything else up
-		 */
+	}
+	/*
+	 * Check for serialization with ide1.
+	 * This code depends on us having already taken care of ide1.
+	 */
+	if (serialized && hwif->name[3] == '0' && ide_hwifs[1].present)
+		hwgroup = ide_hwifs[1].hwgroup;
+	/*
+	 * If this is the first interface in a group,
+	 * then we need to create the hwgroup structure
+	 */
+	if (hwgroup == NULL) {
 		hwgroup = kmalloc (sizeof(ide_hwgroup_t), GFP_KERNEL);
-		irq_to_hwgroup[hwif->irq] = hwgroup;
 		hwgroup->hwif 	 = hwif->next = hwif;
 		hwgroup->rq      = NULL;
 		hwgroup->handler = NULL;
@@ -2783,18 +2838,14 @@
 		hwgroup->hwif->next = hwif;
 	}
 	hwif->hwgroup = hwgroup;
+	irq_to_hwgroup[irq] = hwgroup;
 
 	restore_flags(flags);	/* safe now that hwif->hwgroup is set up */
 
 	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
-		hwif->io_base, hwif->io_base+7, hwif->ctl_port, hwif->irq);
-	if (hwgroup->hwif != hwif) {
-		char *name = hwgroup->hwif->name;
-		if (hwgroup->hwif->irq == hwif->irq)
-			printk(" (shared with %s)", name);
-		else
-			printk(" (serialized with %s)", name);
-	}
+		hwif->io_base, hwif->io_base+7, hwif->ctl_port, irq);
+	if (hwgroup->hwif != hwif)
+		printk(" (serialized with %s)", hwgroup->hwif->name);
 	printk("\n");
 	return 0;
 }
@@ -2826,7 +2877,7 @@
 void buggy_interface_fallback (int rc)
 {
 	ide_pci_access_error (rc);
-	single_threaded = 1;
+	serialized = 1;
 	disallow_unmask = 1;
 	printk("serialized, disabled unmasking\n");
 }
@@ -2859,7 +2910,7 @@
 	int rc;
 	unsigned char reg;
 
-	single_threaded = 1;
+	serialized = 1;
 	printk("ide: buggy CMD640 interface: ");
 
 #if 0	/* funny.. the cmd640b I tried this on claimed to not be enabled.. */
@@ -2869,7 +2920,6 @@
 	} else if (!(sreg & 1)) {
 		printk("not enabled\n");
 	} else {
-#endif /* 0 */
 
 	/*
 	 * The first part is undocumented magic from the DOS driver.
@@ -2879,6 +2929,7 @@
 	if (pcibios_write_config_byte(bus, fn, 0x5b, 0xbd) != 0xbd)
 		printk("init_cmd640: huh? 0x5b read back wrong\n");
 	(void) pcibios_write_config_byte(bus, fn, 0x5b, 0);
+#endif /* 0 */
 	/*
 	 * The rest is from the cmd640b datasheet.
 	 */
@@ -2988,15 +3039,6 @@
 		if (!hwif->present)
 			continue;
 		hwif->present = 0; /* we set it back to 1 if all is ok below */
-		if (h == 0 && single_threaded) {
-			if (ide_hwifs[1].present) {
-				if (irq_to_hwgroup[hwif->irq] != NULL) {
-					printk("%s: SERIALIZE BUG!\n", hwif->name);
-					continue;
-				}
-				irq_to_hwgroup[hwif->irq] = irq_to_hwgroup[ide_hwifs[1].irq];
-			}
-		}
 		switch (hwif->major) {
 			case IDE0_MAJOR: rfn = &do_ide0_request; break;
 			case IDE1_MAJOR: rfn = &do_ide1_request; break;
diff -u --recursive --new-file v1.3.35/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v1.3.35/linux/drivers/block/ide.h	Mon Sep 18 14:53:50 1995
+++ linux/drivers/block/ide.h	Mon Oct 23 13:42:32 1995
@@ -41,6 +41,9 @@
 #ifndef SUPPORT_HT6560B			/* 1 to support HT6560B chipset */
 #define SUPPORT_HT6560B		1	/* 0 to reduce kernel size */
 #endif
+#ifndef SUPPORT_QD6580			/* 1 to support QD6580 chipset */
+#define SUPPORT_QD6580		1	/* 0 to reduce kernel size */
+#endif
 #ifndef SUPPORT_DTC2278			/* 1 to support DTC2278 chipset */
 #define SUPPORT_DTC2278		1	/* 0 to reduce kernel size */
 #ifndef SET_DTC2278_MODE4
@@ -350,7 +353,7 @@
  * One final include file, which references some of the data/defns from above
  */
 #define IDE_DRIVER	/* "parameter" for blk.h */
-#include "blk.h"
+#include <linux/blk.h>
 
 #if (DISK_RECOVERY_TIME > 0)
 void ide_set_recovery_timer (ide_hwif_t *);
diff -u --recursive --new-file v1.3.35/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.35/linux/drivers/block/ll_rw_blk.c	Tue Oct 10 18:46:32 1995
+++ linux/drivers/block/ll_rw_blk.c	Sat Oct 21 19:37:35 1995
@@ -19,7 +19,7 @@
 
 #include <asm/system.h>
 #include <asm/io.h>
-#include "blk.h"
+#include <linux/blk.h>
 
 /*
  * The request-struct contains all necessary data
diff -u --recursive --new-file v1.3.35/linux/drivers/block/mcd.c linux/drivers/block/mcd.c
--- v1.3.35/linux/drivers/block/mcd.c	Tue Oct 10 18:46:32 1995
+++ linux/drivers/block/mcd.c	Thu Jan  1 02:00:00 1970
@@ -1,1632 +0,0 @@
-/*
-	linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
-
-	Copyright (C) 1992  Martin Harriss
-
-	martin@bdsi.com (no longer valid - where are you now, Martin?)
-
-	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; if not, write to the Free Software
-	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-	HISTORY
-
-	0.1	First attempt - internal use only
-	0.2	Cleaned up delays and use of timer - alpha release
-	0.3	Audio support added
-	0.3.1 Changes for mitsumi CRMC LU005S march version
-		   (stud11@cc4.kuleuven.ac.be)
-        0.3.2 bug fixes to the ioctls and merged with ALPHA0.99-pl12
-		   (Jon Tombs <jon@robots.ox.ac.uk>)
-        0.3.3 Added more #defines and mcd_setup()
-   		   (Jon Tombs <jon@gtex02.us.es>)
-
-	October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
-	Braunschweig, Germany: rework to speed up data read operation.
-	Also enabled definition of irq and address from bootstrap, using the
-	environment.
-	November 93 added code for FX001 S,D (single & double speed).
-	February 94 added code for broken M 5/6 series of 16-bit single speed.
-
-
-        0.4   
-        Added support for loadable MODULEs, so mcd can now also be loaded by 
-        insmod and removed by rmmod during runtime.
-        Werner Zimmermann (zimmerma@rz.fht-esslingen.de), Mar. 26, 95
-
-	0.5
-	I added code for FX001 D to drop from double speed to single speed 
-	when encountering errors... this helps with some "problematic" CD's
-	that are supposedly "OUT OF TOLERANCE" (but are really shitty presses!)
-	severly scratched, or possibly slightly warped! I have noticed that
-	the Mitsumi 2x/4x drives are just less tolerant and the firmware is 
-	not smart enough to drop speed,	so let's just kludge it with software!
-	****** THE 4X SPEED MITSUMI DRIVES HAVE THE SAME PROBLEM!!!!!! ******
-	Anyone want to "DONATE" one to me?! ;) I hear sometimes they are
-	even WORSE! ;)
-	** HINT... HINT... TAKE NOTES MITSUMI This could save some hassels with
-	certain "large" CD's that have data on the outside edge in your 
-	DOS DRIVERS .... Accuracy counts... speed is secondary ;)
-	17 June 95 Modifications By Andrew J. Kroll <ag784@freenet.buffalo.edu>
-	07 July 1995 Modifications by Andrew J. Kroll
-
-*/
-
-#include <linux/config.h>
-
-#ifdef MODULE
-# include <linux/module.h>
-# include <linux/version.h>
-# ifndef CONFIG_MODVERSIONS
-    char kernel_version[]= UTS_RELEASE;
-# endif
-#define mcd_init init_module
-#else
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
-#endif
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-
-/* #define REALLY_SLOW_IO  */
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#define MAJOR_NR MITSUMI_CDROM_MAJOR
-#include "blk.h"
-#define mcd_port mcd    /* for compatible parameter passing with "insmod" */
-#include <linux/mcd.h>
-
-#if 0
-static int mcd_sizes[] = { 0 };
-#endif
-
-/* 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; 
-
-/* How many sectors to hold at 1x speed counter */
-static int mcd1xhold = 0;
-
-/* Is the drive connected properly and responding?? */
-static int mcdPresent = 0;
-
-#if 0
-#define TEST1 /* <int-..> */
-#define TEST2 /* do_mcd_req */
-#define TEST3 */ /* MCD_S_state */
-#define TEST4 /* QUICK_LOOP-counter */
-#define TEST5 */ /* port(1) state */
-#endif
-
-#if 1
-#define QUICK_LOOP_DELAY udelay(45)  /* use udelay */
-#define QUICK_LOOP_COUNT 20
-#else
-#define QUICK_LOOP_DELAY
-#define QUICK_LOOP_COUNT 140 /* better wait constant time */
-#endif
-/* #define DOUBLE_QUICK_ONLY */
-
-#define CURRENT_VALID \
-  (CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
-   && CURRENT -> sector != -1)
-#define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
-#define MCD_BUF_SIZ 16
-static volatile int mcd_transfer_is_active;
-static char mcd_buf[2048*MCD_BUF_SIZ];	/* buffer for block size conversion */
-static volatile int mcd_buf_bn[MCD_BUF_SIZ], mcd_next_bn;
-static volatile int mcd_buf_in, mcd_buf_out = -1;
-static volatile int mcd_error;
-static int mcd_open_count;
-enum mcd_state_e {
-  MCD_S_IDLE,   /* 0 */
-  MCD_S_START,  /* 1 */
-  MCD_S_MODE, /* 2 */
-  MCD_S_READ,   /* 3 */
-  MCD_S_DATA,   /* 4 */
-  MCD_S_STOP,   /* 5 */
-  MCD_S_STOPPING /* 6 */
-};
-static volatile enum mcd_state_e mcd_state = MCD_S_IDLE;
-static int mcd_mode = -1;
-static int MCMD_DATA_READ= MCMD_PLAY_READ;
-#define READ_TIMEOUT 3000
-#define WORK_AROUND_MITSUMI_BUG_92
-#define WORK_AROUND_MITSUMI_BUG_93
-#ifdef WORK_AROUND_MITSUMI_BUG_93
-int mitsumi_bug_93_wait = 0;
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-
-static short mcd_port = MCD_BASE_ADDR; /* used as "mcd" by "insmod" */
-static int   mcd_irq  = MCD_INTR_NR; /* must directly follow mcd_port */
-
-static int McdTimeout, McdTries;
-static struct wait_queue *mcd_waitq = NULL;
-
-static struct mcd_DiskInfo DiskInfo;
-static struct mcd_Toc Toc[MAX_TRACKS];
-static struct mcd_Play_msf mcd_Play;
-
-static int audioStatus;
-static char mcdDiskChanged;
-static char tocUpToDate;
-static char mcdVersion;
-
-static void mcd_transfer(void);
-static void mcd_poll(void);
-static void mcd_invalidate_buffers(void);
-static void hsg2msf(long hsg, struct msf *msf);
-static void bin2bcd(unsigned char *p);
-static int bcd2bin(unsigned char bcd);
-static int mcdStatus(void);
-static void sendMcdCmd(int cmd, struct mcd_Play_msf *params);
-static int getMcdStatus(int timeout);
-static int GetQChannelInfo(struct mcd_Toc *qp);
-static int updateToc(void);
-static int GetDiskInfo(void);
-static int GetToc(void);
-static int getValue(unsigned char *result);
-
-
-void mcd_setup(char *str, int *ints)
-{
-   if (ints[0] > 0)
-      mcd_port = ints[1];
-   if (ints[0] > 1)      
-      mcd_irq  = ints[2];
-#ifdef WORK_AROUND_MITSUMI_BUG_93
-   if (ints[0] > 2)
-      mitsumi_bug_93_wait = ints[3];
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-}
-
- 
-static int
-check_mcd_change(kdev_t full_dev)
-{
-   int retval, target;
-
-
-#if 1	 /* the below is not reliable */
-   return 0;
-#endif  
-   target = MINOR(full_dev);
-
-   if (target > 0) {
-      printk("mcd: Mitsumi CD-ROM request error: invalid device.\n");
-      return 0;
-   }
-
-   retval = mcdDiskChanged;
-   mcdDiskChanged = 0;
-
-   return retval;
-}
-
-
-/*
- * Do a 'get status' command and get the result.  Only use from the top half
- * because it calls 'getMcdStatus' which sleeps.
- */
-
-static int
-statusCmd(void)
-{
-	int st, retry;
-
-	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
-	{
-
-		outb(MCMD_GET_STATUS, MCDPORT(0));	/* send get-status cmd */
-		st = getMcdStatus(MCD_STATUS_DELAY);
-		if (st != -1)
-			break;
-	}
-
-	return st;
-}
-
-
-/*
- * Send a 'Play' command and get the status.  Use only from the top half.
- */
-
-static int
-mcdPlay(struct mcd_Play_msf *arg)
-{
-	int retry, st;
-
-	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
-	{
-		sendMcdCmd(MCMD_PLAY_READ, arg);
-		st = getMcdStatus(2 * MCD_STATUS_DELAY);
-		if (st != -1)
-			break;
-	}
-
-	return st;
-}
-
-
-long
-msf2hsg(struct msf *mp)
-{
-	return bcd2bin(mp -> frame)
-		+ bcd2bin(mp -> sec) * 75
-		+ bcd2bin(mp -> min) * 4500
-		- 150;
-}
-
-
-static int
-mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
-						unsigned long 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 mcd_Toc *tocPtr;
-	struct cdrom_subchnl subchnl;
-	struct cdrom_volctrl volctrl;
-
-	if (!ip)
-		return -EINVAL;
-
-	st = statusCmd();
-	if (st < 0)
-		return -EIO;
-
-	if (!tocUpToDate)
-	{
-		i = updateToc();
-		if (i < 0)
-			return i;	/* error reading TOC */
-	}
-
-	switch (cmd)
-	{
-	case CDROMSTART:     /* Spin up the drive */
-		/* Don't think we can do this.  Even if we could,
- 		 * I think the drive times out and stops after a while
-		 * anyway.  For now, ignore it.
-		 */
-
-		return 0;
-
-	case CDROMSTOP:      /* Spin down the drive */
-		outb(MCMD_STOP, MCDPORT(0));
-		i = getMcdStatus(MCD_STATUS_DELAY);
-
-		/* should we do anything if it fails? */
-
-		audioStatus = CDROM_AUDIO_NO_STATUS;
-		return 0;
-
-	case CDROMPAUSE:     /* Pause the drive */
-		if (audioStatus != CDROM_AUDIO_PLAY)
-			return -EINVAL;
-
-		outb(MCMD_STOP, MCDPORT(0));
-		i = getMcdStatus(MCD_STATUS_DELAY);
-
-		if (GetQChannelInfo(&qInfo) < 0)
-		{
-			/* didn't get q channel info */
-
-			audioStatus = CDROM_AUDIO_NO_STATUS;
-			return 0;
-		}
-
-		mcd_Play.start = qInfo.diskTime;	/* remember restart point */
-
-		audioStatus = CDROM_AUDIO_PAUSED;
-		return 0;
-
-	case CDROMRESUME:    /* Play it again, Sam */
-		if (audioStatus != CDROM_AUDIO_PAUSED)
-			return -EINVAL;
-
-		/* restart the drive at the saved position. */
-
-		i = mcdPlay(&mcd_Play);
-		if (i < 0)
-		{
-			audioStatus = CDROM_AUDIO_ERROR;
-			return -EIO;
-		}
-
-		audioStatus = CDROM_AUDIO_PLAY;
-		return 0;
-
-	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
-
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
-		if (st)
-			return st;
-
-		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
-
-		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;
-
-		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",
-	mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
-	mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
-#endif
-
-		i = mcdPlay(&mcd_Play);
-		if (i < 0)
-		{
-			audioStatus = CDROM_AUDIO_ERROR;
-			return -EIO;
-		}
-
-		audioStatus = CDROM_AUDIO_PLAY;
-		return 0;
-
-	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
-
-		if (audioStatus == CDROM_AUDIO_PLAY) {
-		  outb(MCMD_STOP, MCDPORT(0));
-		  i = getMcdStatus(MCD_STATUS_DELAY);
-		  audioStatus = CDROM_AUDIO_NO_STATUS;
-		}
-
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
-		if (st)
-			return st;
-
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-
-		/* 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;
-
-#ifdef MCD_DEBUG
-printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
-mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
-mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
-#endif
-
-		i = mcdPlay(&mcd_Play);
-		if (i < 0)
-		{
-			audioStatus = CDROM_AUDIO_ERROR;
-			return -EIO;
-		}
-
-		audioStatus = CDROM_AUDIO_PLAY;
-		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;
-		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
-		return 0;
-
-	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
-
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
-		if (st)
-			return st;
-
-		memcpy_fromfs(&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)
-			return -EINVAL;
-
-		else
-			tocPtr = &Toc[entry.cdte_track];
-
-		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);
-
-		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);
-		}
-
-		else
-			return -EINVAL;
-
-		memcpy_tofs((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;
-
-		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
-
-		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;
-
-		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
-		return 0;
-
-	case CDROMVOLCTRL:   /* Volume control */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
-		if (st)
-			return st;
-
-		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
-		outb(MCMD_SET_VOLUME, MCDPORT(0));
-		outb(volctrl.channel0, MCDPORT(0));
-		outb(255, MCDPORT(0));
-		outb(volctrl.channel1, MCDPORT(0));
-		outb(255, MCDPORT(0));
-
-		i = getMcdStatus(MCD_STATUS_DELAY);
-		if (i < 0)
-			return -EIO;
-
-		{
-			char a, b, c, d;
-
-			getValue(&a);
-			getValue(&b);
-			getValue(&c);
-			getValue(&d);
-		}
-
-		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.
- */
-
-static void
-mcd_transfer(void)
-{
-  if (CURRENT_VALID) {
-    while (CURRENT -> nr_sectors) {
-      int bn = CURRENT -> sector / 4;
-      int i;
-      for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn; ++i)
-	;
-      if (i < MCD_BUF_SIZ) {
-	int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
-	int nr_sectors = 4 - (CURRENT -> sector & 3);
-	if (mcd_buf_out != i) {
-	  mcd_buf_out = i;
-	  if (mcd_buf_bn[i] != bn) {
-	    mcd_buf_out = -1;
-	    continue;
-	  }
-	}
-	if (nr_sectors > CURRENT -> nr_sectors)
-	  nr_sectors = CURRENT -> nr_sectors;
-	memcpy(CURRENT -> buffer, mcd_buf + offs, nr_sectors * 512);
-	CURRENT -> nr_sectors -= nr_sectors;
-	CURRENT -> sector += nr_sectors;
-	CURRENT -> buffer += nr_sectors * 512;
-      } else {
-	mcd_buf_out = -1;
-	break;
-      }
-    }
-  }
-}
-
-
-/*
- * We only seem to get interrupts after an error.
- * Just take the interrupt and clear out the status reg.
- */
-
-static void
-mcd_interrupt(int irq, struct pt_regs * regs)
-{
-	int st;
-
-	st = inb(MCDPORT(1)) & 0xFF;
-#ifdef TEST1
-		printk("<int1-%02X>", st);
-#endif
-	if (!(st & MFL_STATUS))
-	{
-		st = inb(MCDPORT(0)) & 0xFF;
-#ifdef TEST1
-		printk("<int0-%02X>", st);
-#endif
-		if ((st & 0xFF) != 0xFF)
-		  mcd_error = st ? st & 0xFF : -1;
-	}
-}
-
-
-static void
-do_mcd_request(void)
-{
-#ifdef TEST2
-  printk(" do_mcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
-#endif
-  mcd_transfer_is_active = 1;
-  while (CURRENT_VALID) {
-    if (CURRENT->bh) {
-      if (!CURRENT->bh->b_lock)
-	panic(DEVICE_NAME ": block not locked");
-    }
-    mcd_transfer();
-    if (CURRENT -> nr_sectors == 0) {
-      end_request(1);
-    } else {
-      mcd_buf_out = -1;		/* Want to read a block not in buffer */
-      if (mcd_state == MCD_S_IDLE) {
-	if (!tocUpToDate) {
-	  if (updateToc() < 0) {
-	    while (CURRENT_VALID)
-	      end_request(0);
-	    break;
-	  }
-	}
-	mcd_state = MCD_S_START;
-	McdTries = 5;
-	SET_TIMER(mcd_poll, 1);
-      }
-      break;
-    }
-  }
-  mcd_transfer_is_active = 0;
-#ifdef TEST2
-  printk(" do_mcd_request ends\n");
-#endif
-}
-
-
-
-static void
-mcd_poll(void)
-{
-  int st;
-
-
-  if (mcd_error) 
-  {
-    if (mcd_error & 0xA5) 
-    {
-      printk("mcd: I/O error 0x%02x", mcd_error);
-      if (mcd_error & 0x80)
-	printk(" (Door open)");
-      if (mcd_error & 0x20)
-	printk(" (Disk changed)");
-      if (mcd_error & 0x04)
-	{
-	printk(" (Read error)"); /* Bitch about the problem. */
-	
-	/* Time to get fancy! If at 2x speed and 1 error, drop to 1x speed! */
-	/* Interesting how it STAYS at MCD_RETRY_ATTEMPTS on first error! */
-	/* But I find that rather HANDY!!! */
-	/* Neat! it REALLY WORKS on those LOW QUALITY CD's!!! Smile! :) */
-	/* AJK [06/17/95] */
-	
-	/* Slap the CD down to single speed! */
-	if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_2X_READ) 
-		{
-		MCMD_DATA_READ = MCMD_PLAY_READ; /* Uhhh, Ummmm, muhuh-huh! */
-		mcd1xhold = SINGLE_HOLD_SECTORS; /* Hey Bevis! */
-		printk(" Speed now 1x");	 /* Pull my finger! */
-		}
-	}
-      printk("\n");
-      mcd_invalidate_buffers();
-#ifdef WARN_IF_READ_FAILURE
-      if (McdTries == MCD_RETRY_ATTEMPTS)
-	printk("mcd: read of block %d failed\n", mcd_next_bn);
-#endif
-      if (!McdTries--) 
-        {
-	/* Nuts! This cd is ready for recycling! */
-	/* When WAS the last time YOU cleaned it CORRECTLY?! */
-	printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
-	if (mcd_transfer_is_active) 
-	{
-	  McdTries = 0;
-	  goto ret;
-	}
-	if (CURRENT_VALID)
-	  end_request(0);
-	McdTries = MCD_RETRY_ATTEMPTS;
-      }
-    }
-    mcd_error = 0;
-    mcd_state = MCD_S_STOP;
-  }
-	/* Switch back to Double speed if enough GOOD sectors were read! */
-	
-	/* Are we a double speed with a crappy CD?! */
-    if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_PLAY_READ)
-    	{
-	/* We ARE a double speed and we ARE bitching! */
-	if (mcd1xhold == 0) /* Okay, Like are we STILL at single speed? */
-		{ /* We need to switch back to double speed now... */
-		MCMD_DATA_READ = MCMD_2X_READ; /* Uhhh... BACK You GO! */
-		printk("mcd: Switching back to 2X speed!\n"); /* Tell 'em! */
-		}
-	else mcd1xhold--; /* No?! Count down the good reads some more... */
-				/* and try, try again! */
-    	}
-
-
-
- immediately:
-  switch (mcd_state) {
-
-
-
-  case MCD_S_IDLE:
-#ifdef TEST3
-    printk("MCD_S_IDLE\n");
-#endif
-    return;
-
-
-
-  case MCD_S_START:
-#ifdef TEST3
-    printk("MCD_S_START\n");
-#endif
-
-    outb(MCMD_GET_STATUS, MCDPORT(0));
-    mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE;
-    McdTimeout = 3000;
-    break;
-
-
-
-  case MCD_S_MODE:
-#ifdef TEST3
-    printk("MCD_S_MODE\n");
-#endif
-
-    if ((st = mcdStatus()) != -1) {
-
-      if (st & MST_DSK_CHG) {
-	mcdDiskChanged = 1;
-	tocUpToDate = 0;
-	mcd_invalidate_buffers();
-      }
-
-    set_mode_immediately:
-
-      if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
-	mcdDiskChanged = 1;
-	tocUpToDate = 0;
-	if (mcd_transfer_is_active) {
-	  mcd_state = MCD_S_START;
-	  goto immediately;
-	}
-	printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
-	mcd_state = MCD_S_IDLE;
-	while (CURRENT_VALID)
-	  end_request(0);
-	return;
-      }
-
-      outb(MCMD_SET_MODE, MCDPORT(0));
-      outb(1, MCDPORT(0));
-      mcd_mode = 1;
-      mcd_state = MCD_S_READ;
-      McdTimeout = 3000;
-
-    }
-    break;
-
-
-
-  case MCD_S_READ:
-#ifdef TEST3
-    printk("MCD_S_READ\n");
-#endif
-
-    if ((st = mcdStatus()) != -1) {
-
-      if (st & MST_DSK_CHG) {
-	mcdDiskChanged = 1;
-	tocUpToDate = 0;
-	mcd_invalidate_buffers();
-      }
-
-    read_immediately:
-
-      if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
-	mcdDiskChanged = 1;
-	tocUpToDate = 0;
-	if (mcd_transfer_is_active) {
-	  mcd_state = MCD_S_START;
-	  goto immediately;
-	}
-	printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
-	mcd_state = MCD_S_IDLE;
-	while (CURRENT_VALID)
-	  end_request(0);
-	return;
-      }
-
-      if (CURRENT_VALID) {
-	struct mcd_Play_msf msf;
-	mcd_next_bn = CURRENT -> sector / 4;
-	hsg2msf(mcd_next_bn, &msf.start);
-	msf.end.min = ~0;
-	msf.end.sec = ~0;
-	msf.end.frame = ~0;
-	sendMcdCmd(MCMD_DATA_READ, &msf);
-	mcd_state = MCD_S_DATA;
-	McdTimeout = READ_TIMEOUT;
-      } else {
-	mcd_state = MCD_S_STOP;
-	goto immediately;
-      }
-
-    }
-    break;
-
-
-  case MCD_S_DATA:
-#ifdef TEST3
-    printk("MCD_S_DATA\n");
-#endif
-
-    st = inb(MCDPORT(1)) & (MFL_STATUSorDATA);
-  data_immediately:
-#ifdef TEST5
-    printk("Status %02x\n",st);
-#endif
-    switch (st) {
-
-    case MFL_DATA:
-#ifdef WARN_IF_READ_FAILURE
-      if (McdTries == 5)
-	printk("mcd: read of block %d failed\n", mcd_next_bn);
-#endif
-      if (!McdTries--) {
-	printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
-	if (mcd_transfer_is_active) {
-	  McdTries = 0;
-	  break;
-	}
-	if (CURRENT_VALID)
-	  end_request(0);
-	McdTries = 5;
-      }
-      mcd_state = MCD_S_START;
-      McdTimeout = READ_TIMEOUT;
-      goto immediately;
-
-    case MFL_STATUSorDATA:
-      break;
-
-    default:
-      McdTries = 5;
-      if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) {
-	mcd_state = MCD_S_STOP;
-	goto immediately;
-      }
-      mcd_buf_bn[mcd_buf_in] = -1;
-      READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048);
-      mcd_buf_bn[mcd_buf_in] = mcd_next_bn++;
-      if (mcd_buf_out == -1)
-	mcd_buf_out = mcd_buf_in;
-      mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1;
-      if (!mcd_transfer_is_active) {
-	while (CURRENT_VALID) {
-	  mcd_transfer();
-	  if (CURRENT -> nr_sectors == 0)
-	    end_request(1);
-	  else
-	    break;
-	}
-      }
-
-      if (CURRENT_VALID
-	  && (CURRENT -> sector / 4 < mcd_next_bn || 
-	      CURRENT -> sector / 4 > mcd_next_bn + 16)) {
-	mcd_state = MCD_S_STOP;
-	goto immediately;
-      }
-      McdTimeout = READ_TIMEOUT;
-#ifdef DOUBLE_QUICK_ONLY
-      if (MCMD_DATA_READ != MCMD_PLAY_READ)
-#endif
-      {
-	int count= QUICK_LOOP_COUNT;
-	while (count--) {
-          QUICK_LOOP_DELAY;
-	  if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) {
-#   ifdef TEST4
-/*	    printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */
-	    printk(" %d ",QUICK_LOOP_COUNT-count);
-#   endif
-	    goto data_immediately;
-	  }
-	}
-#   ifdef TEST4
-/*      printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */
-	printk("ended ");
-#   endif
-      }
-      break;
-    }
-    break;
-
-
-
-  case MCD_S_STOP:
-#ifdef TEST3
-    printk("MCD_S_STOP\n");
-#endif
-
-#ifdef WORK_AROUND_MITSUMI_BUG_93
-    if (!mitsumi_bug_93_wait)
-      goto do_not_work_around_mitsumi_bug_93_1;
-
-    McdTimeout = mitsumi_bug_93_wait;
-    mcd_state = 9+3+1;
-    break;
-
-  case 9+3+1:
-    if (McdTimeout)
-      break;
-
-  do_not_work_around_mitsumi_bug_93_1:
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-
-    outb(MCMD_STOP, MCDPORT(0));
-
-#ifdef WORK_AROUND_MITSUMI_BUG_92
-    if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
-      int i = 4096;
-      do {
-	inb(MCDPORT(0));
-      } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
-      outb(MCMD_STOP, MCDPORT(0));
-      if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
-	i = 4096;
-	do {
-	  inb(MCDPORT(0));
-	} while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
-	outb(MCMD_STOP, MCDPORT(0));
-      }
-    }
-#endif /* WORK_AROUND_MITSUMI_BUG_92 */
-
-    mcd_state = MCD_S_STOPPING;
-    McdTimeout = 1000;
-    break;
-
-  case MCD_S_STOPPING:
-#ifdef TEST3
-    printk("MCD_S_STOPPING\n");
-#endif
-
-    if ((st = mcdStatus()) == -1 && McdTimeout)
-      break;
-
-    if ((st != -1) && (st & MST_DSK_CHG)) {
-      mcdDiskChanged = 1;
-      tocUpToDate = 0;
-      mcd_invalidate_buffers();
-    }
-
-#ifdef WORK_AROUND_MITSUMI_BUG_93
-    if (!mitsumi_bug_93_wait)
-      goto do_not_work_around_mitsumi_bug_93_2;
-
-    McdTimeout = mitsumi_bug_93_wait;
-    mcd_state = 9+3+2;
-    break;
-
-  case 9+3+2:
-    if (McdTimeout)
-      break;
-
-    st = -1;
-
-  do_not_work_around_mitsumi_bug_93_2:
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-
-#ifdef TEST3
-    printk("CURRENT_VALID %d mcd_mode %d\n",
-	   CURRENT_VALID, mcd_mode);
-#endif
-
-    if (CURRENT_VALID) {
-      if (st != -1) {
-	if (mcd_mode == 1)
-	  goto read_immediately;
-	else
-	  goto set_mode_immediately;
-      } else {
-	mcd_state = MCD_S_START;
-	McdTimeout = 1;
-      }
-    } else {
-      mcd_state = MCD_S_IDLE;
-      return;
-    }
-    break;
-
-  default:
-    printk("mcd: invalid state %d\n", mcd_state);
-    return;
-  }
-
- ret:
-  if (!McdTimeout--) {
-    printk("mcd: timeout in state %d\n", mcd_state);
-    mcd_state = MCD_S_STOP;
-  }
-
-  SET_TIMER(mcd_poll, 1);
-}
-
-
-
-static void
-mcd_invalidate_buffers(void)
-{
-  int i;
-  for (i = 0; i < MCD_BUF_SIZ; ++i)
-    mcd_buf_bn[i] = -1;
-  mcd_buf_out = -1;
-}
-
-
-/*
- * Open the device special file.  Check that a disk is in.
- */
-
-int
-mcd_open(struct inode *ip, struct file *fp)
-{
-	int st;
-
-	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();
-
-	st = statusCmd();			/* check drive status */
-	if (st == -1)
-		return -EIO;			/* drive doesn't respond */
-
-	if ((st & MST_READY) == 0)		/* no disk in drive */
-	{
-		printk("mcd: no disk in drive\n");
-		return -EIO;
-	}
-
-	if (updateToc() < 0)
-		return -EIO;
-
-	}
-	++mcd_open_count;
-        MOD_INC_USE_COUNT;
-	return 0;
-}
-
-
-/*
- * On close, we flush all mcd blocks from the buffer cache.
- */
-
-static void
-mcd_release(struct inode * inode, struct file * file)
-{ MOD_DEC_USE_COUNT;
-  if (!--mcd_open_count) {
-	mcd_invalidate_buffers();
-	sync_dev(inode->i_rdev);
-	invalidate_buffers(inode -> i_rdev);
-  }
-}
-
-
-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,			/* select */
-	mcd_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	mcd_open,		/* open */
-	mcd_release,		/* release */
-	NULL,			/* fsync */
-	NULL,			/* fasync */
-	check_mcd_change,	/* media change */
-	NULL			/* revalidate */
-};
-
-
-/*
- * Test for presence of drive and initialize it.  Called at boot time.
- */
-
-int
-mcd_init(void)
-{
-	int count;
-	unsigned char result[3];
-
-	if (mcd_port <= 0 || mcd_irq <= 0) {
-	  printk("skip mcd_init\n");
-          return -EIO;
-	}
-
-	printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
-
-	if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
-	{
-		printk("Unable to get major %d for Mitsumi CD-ROM\n",
-		       MAJOR_NR);
-                return -EIO;
-	}
-
-        if (check_region(mcd_port, 4)) {
-	  printk("Init failed, I/O port (%X) already in use\n",
-		 mcd_port);
-          return -EIO;
-	}
-	  
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = 4;
-
-	/* check for card */
-
-	outb(0, MCDPORT(1));			/* send reset */
-	for (count = 0; count < 2000000; count++)
-		(void) inb(MCDPORT(1));		/* delay a bit */
-
-	outb(0x40, MCDPORT(0));			/* send get-stat cmd */
-	for (count = 0; count < 2000000; count++)
-		if (!(inb(MCDPORT(1)) & MFL_STATUS))
-			break;
-
-	if (count >= 2000000) {
-		printk("Init failed. No mcd device at 0x%x irq %d\n",
-		     mcd_port, mcd_irq);
-                return -EIO;
-	}
-	count = inb(MCDPORT(0));		/* pick up the status */
-	
-	outb(MCMD_GET_VERSION,MCDPORT(0));
-	for(count=0;count<3;count++)
-		if(getValue(result+count)) {
-			printk("mitsumi get version failed at 0x%d\n",
-			       mcd_port);
-                        return -EIO;
-		}	
-
-	if (result[0] == result[1] && result[1] == result[2])
-                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];
-
-	if (mcdVersion >=4)
-		outb(4,MCDPORT(2)); 	/* magic happens */
-
-	/* don't get the IRQ until we know for sure the drive is there */
-
-	if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD"))
-	{
-		printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
-                return -EIO;
-	}
-	request_region(mcd_port, 4,"mcd");
-
-	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
-	outb(0x02,MCDPORT(0));
-	outb(0x00,MCDPORT(0));
-	getValue(result);
-
-	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
-	outb(0x10,MCDPORT(0));
-	outb(0x04,MCDPORT(0));
-	getValue(result);
-
-	mcd_invalidate_buffers();
-	mcdPresent = 1;
-        return 0;
-}
-
-
-static void
-hsg2msf(long hsg, struct msf *msf)
-{
-	hsg += 150;
-	msf -> min = hsg / 4500;
-	hsg %= 4500;
-	msf -> sec = hsg / 75;
-	msf -> frame = hsg % 75;
-
-	bin2bcd(&msf -> min);		/* convert to BCD */
-	bin2bcd(&msf -> sec);
-	bin2bcd(&msf -> frame);
-}
-
-
-static void
-bin2bcd(unsigned char *p)
-{
-	int u, t;
-
-	u = *p % 10;
-	t = *p / 10;
-	*p = u | (t << 4);
-}
-
-static int
-bcd2bin(unsigned char bcd)
-{
-	return (bcd >> 4) * 10 + (bcd & 0xF);
-}
-
-
-/*
- * See if a status is ready from the drive and return it
- * if it is ready.
- */
-
-static int
-mcdStatus(void)
-{
-	int i;
-	int st;
-
-	st = inb(MCDPORT(1)) & MFL_STATUS;
-	if (!st)
-	{
-		i = inb(MCDPORT(0)) & 0xFF;
-		return i;
-	}
-	else
-		return -1;
-}
-
-
-/*
- * Send a play or read command to the drive
- */
-
-static void
-sendMcdCmd(int cmd, struct mcd_Play_msf *params)
-{
-	outb(cmd, MCDPORT(0));
-	outb(params -> start.min, MCDPORT(0));
-	outb(params -> start.sec, MCDPORT(0));
-	outb(params -> start.frame, MCDPORT(0));
-	outb(params -> end.min, MCDPORT(0));
-	outb(params -> end.sec, MCDPORT(0));
-	outb(params -> end.frame, MCDPORT(0));
-}
-
-
-/*
- * Timer interrupt routine to test for status ready from the drive.
- * (see the next routine)
- */
-
-static void
-mcdStatTimer(void)
-{
-	if (!(inb(MCDPORT(1)) & MFL_STATUS))
-	{
-		wake_up(&mcd_waitq);
-		return;
-	}
-
-	McdTimeout--;
-	if (McdTimeout <= 0)
-	{
-		wake_up(&mcd_waitq);
-		return;
-	}
-
-	SET_TIMER(mcdStatTimer, 1);
-}
-
-
-/*
- * Wait for a status to be returned from the drive.  The actual test
- * (see routine above) is done by the timer interrupt to avoid
- * excessive rescheduling.
- */
-
-static int
-getMcdStatus(int timeout)
-{
-	int st;
-
-	McdTimeout = timeout;
-	SET_TIMER(mcdStatTimer, 1);
-	sleep_on(&mcd_waitq);
-	if (McdTimeout <= 0)
-		return -1;
-
-	st = inb(MCDPORT(0)) & 0xFF;
-	if (st == 0xFF)
-		return -1;
-
-	if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY)
-		/* XXX might be an error? look at q-channel? */
-		audioStatus = CDROM_AUDIO_COMPLETED;
-
-	if (st & MST_DSK_CHG)
-	{
-		mcdDiskChanged = 1;
-		tocUpToDate = 0;
-		audioStatus = CDROM_AUDIO_NO_STATUS;
-	}
-
-	return st;
-}
-
-
-/*
- * Read a value from the drive.  Should return quickly, so a busy wait
- * is used to avoid excessive rescheduling.
- */
-
-static int
-getValue(unsigned char *result)
-{
-	int count;
-	int s;
-
-	for (count = 0; count < 2000; count++)
-		if (!(inb(MCDPORT(1)) & MFL_STATUS))
-			break;
-
-	if (count >= 2000)
-	{
-		printk("mcd: getValue timeout\n");
-		return -1;
-	}
-
-	s = inb(MCDPORT(0)) & 0xFF;
-	*result = (unsigned char) s;
-	return 0;
-}
-
-
-/*
- * Read the current Q-channel info.  Also used for reading the
- * table of contents.
- */
-
-int
-GetQChannelInfo(struct mcd_Toc *qp)
-{
-	unsigned char notUsed;
-	int retry;
-
-	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
-	{
-		outb(MCMD_GET_Q_CHANNEL, MCDPORT(0));
-		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
-			break;
-	}
-
-	if (retry >= MCD_RETRY_ATTEMPTS)
-		return -1;
-
-	if (getValue(&qp -> ctrl_addr) < 0) return -1;
-	if (getValue(&qp -> track) < 0) return -1;
-	if (getValue(&qp -> pointIndex) < 0) return -1;
-	if (getValue(&qp -> trackTime.min) < 0) return -1;
-	if (getValue(&qp -> trackTime.sec) < 0) return -1;
-	if (getValue(&qp -> trackTime.frame) < 0) return -1;
-	if (getValue(&notUsed) < 0) return -1;
-	if (getValue(&qp -> diskTime.min) < 0) return -1;
-	if (getValue(&qp -> diskTime.sec) < 0) return -1;
-	if (getValue(&qp -> diskTime.frame) < 0) return -1;
-
-	return 0;
-}
-
-
-/*
- * Read the table of contents (TOC) and TOC header if necessary
- */
-
-static int
-updateToc()
-{
-	if (tocUpToDate)
-		return 0;
-
-	if (GetDiskInfo() < 0)
-		return -EIO;
-
-	if (GetToc() < 0)
-		return -EIO;
-
-	tocUpToDate = 1;
-	return 0;
-}
-
-
-/*
- * Read the table of contents header
- */
-
-static int
-GetDiskInfo()
-{
-	int retry;
-
-	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
-	{
-		outb(MCMD_GET_DISK_INFO, MCDPORT(0));
-		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
-			break;
-	}
-
-	if (retry >= MCD_RETRY_ATTEMPTS)
-		return -1;
-
-	if (getValue(&DiskInfo.first) < 0) return -1;
-	if (getValue(&DiskInfo.last) < 0) return -1;
-
-	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,
-	DiskInfo.last,
-	DiskInfo.diskLength.min,
-	DiskInfo.diskLength.sec,
-	DiskInfo.diskLength.frame,
-	DiskInfo.firstTrack.min,
-	DiskInfo.firstTrack.sec,
-	DiskInfo.firstTrack.frame);
-#endif
-
-	return 0;
-}
-
-
-/*
- * Read the table of contents (TOC)
- */
-
-static int
-GetToc()
-{
-	int i, px;
-	int limit;
-	int retry;
-	struct mcd_Toc qInfo;
-
-	for (i = 0; i < MAX_TRACKS; i++)
-		Toc[i].pointIndex = 0;
-
-	i = DiskInfo.last + 3;
-
-	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
-	{
-		outb(MCMD_STOP, MCDPORT(0));
-		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
-			break;
-	}
-
-	if (retry >= MCD_RETRY_ATTEMPTS)
-		return -1;
-
-	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
-	{
-		outb(MCMD_SET_MODE, MCDPORT(0));
-		outb(0x05, MCDPORT(0));			/* mode: toc */
-		mcd_mode = 0x05;
-		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
-			break;
-	}
-
-	if (retry >= MCD_RETRY_ATTEMPTS)
-		return -1;
-
-	for (limit = 300; limit > 0; limit--)
-	{
-		if (GetQChannelInfo(&qInfo) < 0)
-			break;
-
-		px = bcd2bin(qInfo.pointIndex);
-		if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
-			if (Toc[px].pointIndex == 0)
-			{
-				Toc[px] = qInfo;
-				i--;
-			}
-
-		if (i <= 0)
-			break;
-	}
-
-	Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-
-	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
-	{
-                outb(MCMD_SET_MODE, MCDPORT(0));
-                outb(0x01, MCDPORT(0));
-		mcd_mode = 1;
-                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
-                        break;
-	}
-
-#ifdef MCD_DEBUG
-for (i = 1; i <= DiskInfo.last; i++)
-printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
-i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-for (i = 100; i < 103; i++)
-printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
-i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-#endif
-
-	return limit > 0 ? 0 : -1;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{ if (MOD_IN_USE)
-     { printk("mcd module in use - can't remove it.\n");
-       return;    
-     }
-  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);
-  printk("mcd module released.\n");
-}
-#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/block/mcdx.c linux/drivers/block/mcdx.c
--- v1.3.35/linux/drivers/block/mcdx.c	Tue Oct 10 18:46:32 1995
+++ linux/drivers/block/mcdx.c	Thu Jan  1 02:00:00 1970
@@ -1,1727 +0,0 @@
-/*
- * The Mitsumi CDROM interface
- * Copyright (C) 1995 Heiko Schlittermann
- * VERSION: 1.0a
- * 
- * 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 Harriss (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 (extensivly tested the LU portion)
- *  ... somebody forgotten?
- *  
- */
-
-
-#if RCS
-static const char *mcdx_c_version
-		= "mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp";
-#endif
-
-#include <linux/config.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[] = UTS_RELEASE;
-#endif
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#define MOD_IN_USE 1
-#endif MODULE
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-
-#include <linux/major.h>
-
-#ifndef MITSUMI_X_CDROM_MAJOR               /* old kernel (doesn't know about MCDX) */
-#define MITSUMI_X_CDROM_MAJOR 20
-#define DEVICE_NAME "Mitsumi CD-ROM"
-/* #define DEVICE_INTR do_mcdx */
-#define DEVICE_REQUEST do_mcdx_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-#endif
-
-#define MAJOR_NR MITSUMI_X_CDROM_MAJOR
-#include "blk.h"
-#define	mcdx_drive_map mcdx     /* for compatible parameter passing with "insmod" */
-#include <linux/mcdx.h>
-
-/* CONSTANTS *******************************************************/
-
-const int REQUEST_SIZE = 200;
-const int DIRECT_SIZE = 200;
-
-enum drivemodes { TOC, DATA, RAW, COOKED };
-enum datamodes { MODE0, MODE1, MODE2 };
-enum resetmodes { SOFT, HARD };
-
-const int SINGLE = 0x01;
-const int DOUBLE = 0x02;
-const int DOOR   = 0x04;
-const int MULTI  = 0x08;
-const int READY  = 0x70;
-
-const unsigned char READSSPEED = 0xc0;
-const unsigned char READDSPEED = 0xc1;
-
-
-/* DECLARATIONS ****************************************************/ 
-struct s_msf {
-	unsigned char minute;
-	unsigned char second;
-	unsigned char frame;
-};
-
-struct s_subqcode {
-	unsigned char control;
-	unsigned char tno;
-	unsigned char index;
-	struct s_msf tt;
-	struct s_msf dt;
-};
-
-struct s_diskinfo {
-	unsigned int n_first;
-	unsigned int n_last;
-	struct s_msf msf_leadout;
-	struct s_msf msf_first;
-};
-	
-struct s_multi {
-	unsigned char multi;
-	struct s_msf msf_last;
-};
-
-struct s_version {
-	unsigned char code;
-	unsigned char ver;
-};
-
-/* Per drive/controller stuff **************************************/
-
-struct s_drive_stuff {
-	/* waitquenes */
-    struct wait_queue *busyq;
-    struct wait_queue *lockq;
-    struct wait_queue *sleepq;
-
- 	/* flags */
-    volatile int introk;	/* status of last irq operation */
-    volatile int busy;		/* drive performs an operation */
-    volatile int lock;		/* exclusive usage */
-
-	/* cd infos */
-	struct s_diskinfo di;
-	struct s_multi multi;
-	struct s_subqcode* toc;	/* first enty of the toc array */
-	struct s_subqcode start;
-    struct s_subqcode stop;
-	int xa;					/* 1 if xa disk */
-	int audio;				/* 1 if audio disk */
-	int audiostatus;			
-
-	/* `buffer' control */
-    volatile int valid;
-    volatile int pending;
-    volatile int off_direct;
-    volatile int off_requested;
-
-	/* adds and odds */
-	void* wreg_data;	/* w data */
-	void* wreg_reset;	/* w hardware reset */
-	void* wreg_hcon;	/* w hardware conf */
-	void* wreg_chn;		/* w channel */
-	void* rreg_data;	/* r data */
-	void* rreg_status;	/* r status */
-
-    int irq;			/* irq used by this drive */
-    int minor;			/* minor number of this drive */
-    int present;	    /* drive present and its capabilities */
-    char readcmd;		/* read cmd depends on single/double speed */
-    char playcmd;       /* play should always be single speed */
-    unsigned long changed;	/* last jiff the media was changed */
-    unsigned long xxx;      /* last jiff it was asked for media change */
-    int users;				/* keeps track of open/close */
-    int lastsector;			/* last block accessible */
-    int errno;				/* last operation's error */
-
-};
-
-
-/* Prototypes ******************************************************/ 
-
-/*	The following prototypes are already declared elsewhere.  They are
- 	repeated here to show what's going on.  And to sense, if they're
-	changed elsewhere. */
-
-/* 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. */
-
-/* ???  exported by the mcdx_sigaction struct */
-static void mcdx_intr(int, struct pt_regs*);
-
-/* exported by file_ops */
-static int mcdx_open(struct inode*, struct file*);
-static void mcdx_close(struct inode*, struct file*);
-static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
-
-/* misc internal support functions */
-static void log2msf(unsigned int, struct s_msf*);
-static unsigned int msf2log(const struct s_msf*);
-static unsigned int uint2bcd(unsigned int);
-static unsigned int bcd2uint(unsigned char);
-#if MCDX_DEBUG
-static void TRACE((int level, const char* fmt, ...));
-#endif
-static void warn(const char* fmt, ...);
-static char *port(int*);
-static int irq(int*);
-static void mcdx_delay(struct s_drive_stuff*, long jifs);
-static int mcdx_transfer(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);
-static int mcdx_requestmultidiskinfo(struct s_drive_stuff*, struct s_multi*, int);
-static int mcdx_requesttocdata(struct s_drive_stuff*, struct s_diskinfo*, int);
-static int mcdx_getstatus(struct s_drive_stuff*, int);
-static int mcdx_getval(struct s_drive_stuff*, int to, int delay, char*);
-
-static int mcdx_talk(struct s_drive_stuff*, 
-		const unsigned char* cmd, size_t, void *buffer,
-		size_t size, unsigned int timeout, int);
-static int mcdx_readtoc(struct s_drive_stuff*);
-static int mcdx_playtrk(struct s_drive_stuff*, const struct cdrom_ti*);
-static int mcdx_playmsf(struct s_drive_stuff*, const struct cdrom_msf*);
-
-/* static variables ************************************************/
-
-static int dummy0;
-static int mcdx_drive_map[][2] = MCDX_DRIVEMAP;
-static struct s_drive_stuff* mcdx_stuffp[MCDX_NDRIVES];
-static struct s_drive_stuff* mcdx_irq_map[16] =
-		{0, 0, 0, 0, 0, 0, 0, 0,
-		0, 0, 0, 0, 0, 0, 0, 0};
-
-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 select */
-	mcdx_ioctl,		/* ioctl() */
-	NULL,			/* no mmap */
-	mcdx_open,		/* open() */
-	mcdx_close,		/* close() */
-	NULL,			/* fsync */
-	NULL,                   /* fasync */
-	check_mcdx_media_change, /* media_change */
-	NULL                    /* revalidate */
-};
-
-/* KERNEL INTERFACE FUNCTIONS **************************************/ 
-
-static int 
-mcdx_ioctl(
-	struct inode* ip, struct file* fp, 
-	unsigned int cmd, unsigned long arg)
-{ 
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
-
-	if (!stuffp->present) return -ENXIO;
-	if (!ip) return -EINVAL;
-
-	switch (cmd) {
-		case CDROMSTART: {
-			TRACE((IOCTL, "ioctl() START\n"));
-			return 0;
-		}
-
-		case CDROMSTOP: {
-			TRACE((IOCTL, "ioctl() STOP\n"));
-            stuffp->audiostatus = CDROM_AUDIO_INVALID;
-			if (-1 == mcdx_stop(stuffp, 1))
-				return -EIO;
-			return 0;
-		}
-
-		case CDROMPLAYTRKIND: {
-			int ans;
-			struct cdrom_ti ti;
-
-			TRACE((IOCTL, "ioctl() PLAYTRKIND\n"));
-			if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti))))
-				return ans;
-			memcpy_fromfs(&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;
-            TRACE((IOCTL, "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;
-
-            TRACE((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;
-
-            memcpy_fromfs(&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_min1 = uint2bcd(msf.cdmsf_min1);
-            msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1);
-            msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1);
-
-            return mcdx_playmsf(stuffp, &msf);
-        }
-
-        case CDROMRESUME: {
-            TRACE((IOCTL, "ioctl() RESUME\n"));
-            return mcdx_playtrk(stuffp, NULL);
-        }
-
-		case CDROMREADTOCENTRY: {
-			struct cdrom_tocentry entry;
-			struct s_subqcode *tp = NULL;
-			int ans;
-
-			TRACE((IOCTL, "ioctl() READTOCENTRY\n"));
-
-            if (-1 == mcdx_readtoc(stuffp)) return -1;
-
-			if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(entry)))) return ans;
-			memcpy_fromfs(&entry, (void *) arg, sizeof(entry));
-
-			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) WARN(("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;
-
-			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(entry)))) return ans;
-			memcpy_tofs((void*) arg, &entry, sizeof(entry));
-
-			return 0;
-		}
-
-		case CDROMSUBCHNL: {
-			int ans;
-			struct cdrom_subchnl sub;
-			struct s_subqcode q;
-
-			TRACE((IOCTL, "ioctl() SUBCHNL\n"));
-
-			if ((ans = verify_area(VERIFY_READ, 
-                    (void*) arg, sizeof(sub)))) return ans;
-
-			memcpy_fromfs(&sub, (void*) arg, sizeof(sub));
-
-			if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO;
-
-            TRACE((IOCTL, "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);
-
-            TRACE((IOCTL, "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);
-                TRACE((IOCTL, "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);
-                TRACE((IOCTL,
-                        "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;
-
-			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(sub))))
-				return ans;
-			memcpy_tofs((void*) arg, &sub, sizeof(sub));
-
-			return 0;
-		}
-
-		case CDROMREADTOCHDR: {
-			struct cdrom_tochdr toc;
-			int ans;
-
-			TRACE((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;
-			memcpy_tofs((void*) arg, &toc, sizeof toc);
-			TRACE((IOCTL, "ioctl() track0 = %d, track1 = %d\n",
-					stuffp->di.n_first, stuffp->di.n_last));
-			return 0;
-		}
-
-		case CDROMPAUSE: {
-			TRACE((IOCTL, "ioctl() PAUSE\n"));
-			if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL;
-			if (-1 == mcdx_stop(stuffp, 1)) return -EIO;
-			if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1))
-				return -EIO;
-            
-            stuffp->audiostatus = CDROM_AUDIO_PAUSED;
-			return 0;
-		}
-
-		case CDROMMULTISESSION: {
-			int ans;
-			struct cdrom_multisession ms;
-			TRACE((IOCTL, "ioctl() MULTISESSION\n"));
-			if (0 != (ans = verify_area(VERIFY_READ, (void*) arg, 
-					sizeof(struct cdrom_multisession))))
-				return ans;
-				
-			memcpy_fromfs(&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->xa;
-
-			if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg,
-					sizeof(struct cdrom_multisession))))
-				return ans;
-
-			memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
-			if (ms.addr_format == CDROM_MSF) 
-				TRACE((IOCTL, 
-						"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
-			  {
-			    dummy0=0;
-			    TRACE((IOCTL, 
-					"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: {
-			TRACE((IOCTL, "ioctl() EJECT\n"));
-			if (stuffp->users > 1) return -EBUSY;
-			if (-1 == mcdx_eject(stuffp, 1)) return -EIO;
-			return 0;
-		}
-
-        case CDROMVOLCTRL: {
-            TRACE((IOCTL, "ioctl() volctrl\n"));
-            return 0;
-        }
-
-		default:
-			WARN(("ioctl(): unknown request 0x%04x\n", cmd));
-	    	return -EINVAL;
-	}
-}
-
-void do_mcdx_request()
-{
-    int dev;
-    struct s_drive_stuff *stuffp;
-
-  again:
-
-	TRACE((REQUEST, "do_request()\n"));
-
-	if ((CURRENT == NULL) || (CURRENT->rq_status == RQ_INACTIVE))  {
-		TRACE((REQUEST, "do_request() done\n"));
-		return;
-	}
-
-    stuffp = mcdx_stuffp[MINOR(CURRENT->rq_dev)];
-	TRACE((REQUEST, "do_request() stuffp = %p\n", stuffp));
-
-    INIT_REQUEST;
-    dev = MINOR(CURRENT->rq_dev);
-
-	if ((dev < 0) || (dev >= MCDX_NDRIVES) || (!stuffp->present)) {
-		WARN(("do_request(): bad device: %s\n",
-		      kdevname(CURRENT->rq_dev)));
-		end_request(0);
-		goto again;
-    }
-
-	if (stuffp->audio) {
-		WARN(("do_request() attempt to read from audio cd\n"));
-		end_request(0);
-		goto again;
-	}
-
-    switch (CURRENT->cmd) {
-      case WRITE:
-	  WARN(("do_request(): attempt to write to cd!!\n"));
-	  end_request(0);
-	  break;
-
-      case READ:
-	  stuffp->errno = 0;
-	  while (CURRENT->nr_sectors) {
-	      int i;
-
-	      if (-1 == (i = mcdx_transfer(
-				      stuffp,
-				      CURRENT->buffer,
-				      CURRENT->sector,
-				      CURRENT->nr_sectors))) {
-          WARN(("do_request() read error\n"));
-		  if (stuffp->errno == MCDX_EOM) {
-		      CURRENT->sector += CURRENT->nr_sectors;
-		      CURRENT->nr_sectors = 0;
-		  }
-		  end_request(0);
-		  goto again;
-	      }
-	      CURRENT->sector += i;
-	      CURRENT->nr_sectors -= i;
-	      CURRENT->buffer += (i * 512);
-
-	  }
-
-	  end_request(1);
-	  break;
-
-      default:
-	  panic(MCDX "do_request: unknown command.\n");
-	  break;
-    }
-
-    goto again;
-}
-
-static int 
-mcdx_open(struct inode *ip, struct file *fp)
-{
-    struct s_drive_stuff *stuffp;
-
-	TRACE((OPENCLOSE, "open()\n"));
-    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
-    if (!stuffp->present) return -ENXIO;
-
-    if (-1 == mcdx_getstatus(stuffp, 1)) return -EIO;
-
-	/* close the door, if necessary (get the door information
-    from the hardware status register) */
-	if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR)  
-		mcdx_closedoor(stuffp, 1);
-
-	/* if the media changed we will have to little more */
-	if (stuffp->xxx < stuffp->changed) {
-
-		TRACE((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. */
-
-        /*
-        TRACE((OPENCLOSE, "open() hardware reset\n"));
-		if (-1 == mcdx_reset(stuffp, HARD, 1)) return -EIO;
-        */
-
-        stuffp->audiostatus = CDROM_AUDIO_INVALID;
-
-		/* get the multisession information */
-		{
-			TRACE((OPENCLOSE, "open() Request multisession info\n"));
-			if (-1 == mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6))
-				return -EIO;
-		
-			if (stuffp->multi.multi > 2)
-				WARN(("open() unknown multisession value (%d)\n", stuffp->multi.multi));
-
-			/* multisession ? */
-			if (!stuffp->multi.multi)
-				stuffp->multi.msf_last.second = 2;
-
-			TRACE((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)) return -EIO;
-
-		stuffp->lastsector = (CD_FRAMESIZE / 512) 
-                * msf2log(&stuffp->di.msf_leadout) - 1;
-
-		TRACE((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)));
-		TRACE((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) {
-			TRACE((MALLOC, "open() free toc @ %p\n", stuffp->toc));
-			kfree(stuffp->toc);
-		}
-		stuffp->toc = NULL;
-
-		TRACE((OPENCLOSE, "open() init irq generation\n"));
-		if (-1 == mcdx_config(stuffp, 1)) return -EIO;
-
-		/* try to get the first sector ... */
-		{
-			char buf[512];
-			int ans;
-			int tries;
-
-			stuffp->xa = 0;
-			stuffp->audio = 0;
-
-			for (tries = 6; tries; tries--) {
-				TRACE((OPENCLOSE, "open() try as %s\n",
-					stuffp->xa ? "XA" : "normal"));
-
-				/* set data mode */
-				if (-1 == (ans = mcdx_setdatamode(stuffp, 
-						stuffp->xa ? MODE2 : MODE1, 1)))
-					return -EIO;
-
-				if ((stuffp->audio = e_audio(ans))) break; 
-
-				while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) 
-					;
-
-				if (ans == 1) break;
-				stuffp->xa = !stuffp->xa; 
-			}
-			if (!tries) return -EIO;
-		}
-
-		/* xa disks will be read in raw mode, others not */
-		if (-1 == mcdx_setdrivemode(stuffp, 
-				stuffp->xa ? RAW : COOKED, 1))
-			return -EIO;
-
-		if (stuffp->audio) {
-			INFO(("open() audio disk found\n"));
-		} else {
-			INFO(("open() %s%s disk found\n",
-					stuffp->xa ? "XA / " : "",
-					stuffp->multi.multi ? "Multi Session" : "Single Session"));
-		}
-
-        stuffp->xxx = jiffies;
-	}
-
-    /* lock the door if not already done */
-    if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1))) 
-        return -EIO;
-
-    stuffp->users++;
-    MOD_INC_USE_COUNT;
-    return 0;
-}
-
-static void 
-mcdx_close(struct inode *ip, struct file *fp)
-{
-    struct s_drive_stuff *stuffp;
-
-    TRACE((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 (-1 == mcdx_lockdoor(stuffp, 0, 1))
-				printk(MCDX ": Cannot unlock the door\n");
-    }
-    MOD_DEC_USE_COUNT;
-
-    return;
-}
-
-int check_mcdx_media_change(kdev_t full_dev)
-/*	Return: 1 if media changed since last call to 
-			  this function
-			0 else
-	Setting flag to 0 resets the changed state. */
-
-{
-    INFO(("check_mcdx_media_change called for device %s\n",
-	  kdevname(full_dev)));
-    return 0;
-}
-
-void mcdx_setup(char *str, int *pi)
-{
-#if MCDX_DEBUG
-    printk(MCDX ":: setup(%s, %d) called\n",
-	    str, pi[0]);
-#endif
-}
-
-/* DIRTY PART ******************************************************/ 
-
-static void mcdx_delay(struct s_drive_stuff *stuff, long jifs)
-/*	This routine is used for sleeping while initialisation - it seems that
- 	there are no other means available. May be we could use a simple count
- 	loop w/ jumps to itself, but I wanna make this independend of cpu
- 	speed. [1 jiffie is 1/HZ sec */
-{
-    unsigned long tout = jiffies + jifs;
-
-    TRACE((INIT, "mcdx_delay %d\n", jifs));
-    if (jifs < 0) return;
-
-#if 1
-    while (jiffies < tout) {
-        current->timeout = jiffies;
-        schedule();
-    }
-#else
-    if (current->pid == 0) {        /* no sleep allowed */
-		while (jiffies < tout) {
-            current->timeout = jiffies;
-            schedule();
-        }
-    } else {                        /* sleeping is allowed */
-        current->timeout = tout;
-        current->state = TASK_INTERRUPTIBLE;
-        while (current->timeout) {
-            interruptible_sleep_on(&stuff->sleepq);
-        }
-    }
-#endif
-}
-
-static void 
-mcdx_intr(int irq, struct pt_regs* regs)
-{
-    struct s_drive_stuff *stuffp;
-	unsigned char x;
-
-    stuffp = mcdx_irq_map[irq];
-
-    if (!stuffp->busy) {
-		INFO(("intr() unexpected interrupt @ irq %d\n", irq));
-		return;
-    }
-
-	/* if not ok read the next byte as the drives status */
-	if (0 == (stuffp->introk = 
-			(~(x = inb((unsigned int) stuffp->rreg_status)) & MCDX_RBIT_DTEN))) 
-		TRACE((IRQ, "intr() irq %d failed, status %02x %02x\n",
-				irq, x, inb((unsigned int) stuffp->rreg_data)));
-	else
-	  {
-	    dummy0=0;
-	    TRACE((IRQ, "irq() irq %d ok, status %02x\n", irq, x));
-	  }
-    stuffp->busy = 0;
-    wake_up_interruptible(&stuffp->busyq);
-}
-
-
-static int 
-mcdx_talk (
-		struct s_drive_stuff *stuffp, 
-		const unsigned char *cmd, size_t cmdlen,
-		void *buffer, size_t size, 
-		unsigned int timeout, int tries)
-/* Send a command to the drive, wait for the result.
- * returns -1 on timeout, drive status otherwise
- */
-{
-	char c;
-	int st;
-
-	if (!buffer || size == 0) buffer = &c, size = 1;
-
-    while (stuffp->lock)
-		interruptible_sleep_on(&stuffp->lockq);
-
-    if (current->signal && ~current->blocked) {
-        WARN(("talk() got signal %d\n", current->signal));
-        return -1;
-    }
-
-    stuffp->lock = 1;
-    stuffp->valid = 0;	
-
-#if MCDX_DEBUG & TALK
-	{ 
-		unsigned char i;
-		TRACE((TALK, "talk() %d / %d tries, res.size %d, command 0x%02x", 
-				tries, timeout, size, (unsigned char) cmd[0]));
-		for (i = 1; i < cmdlen; i++) printk(" 0x%02x", cmd[i]);
-		printk("\n");
-	}
-#endif
-
-    /*  give up if all tries are done (bad) or if the status
-     *  st != -1 (good) */
-	for (st = -1; st == -1 && tries; tries--) {
-
-        size_t sz = size;
-        char* bp = buffer;
-
-		outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen);
-        TRACE((TALK, "talk() command sent\n"));
-
-        /* get the status byte */
-        if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
-            WARN(("talk() %02x timed out (status), %d tr%s left\n", 
-                    cmd[0], tries - 1, tries == 2 ? "y" : "ies"));
-                continue; 
-        }
-        st = *bp++;
-        sz--;
-
-        TRACE((TALK, "talk() got status 0x%02x\n", st));
-
-        /* command error? */
-        if (e_cmderr(st)) {
-            WARN(("command error %02x (%d)\n", cmd[0], cmdlen));
-            st = -1;
-            continue;
-        }
-
-        /* audio status? */
-        if (stuffp->audiostatus == CDROM_AUDIO_INVALID)
-            stuffp->audiostatus = 
-                    e_audiobusy(st) ? CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS;
-
-        /* media change? */
-        if (e_changed(st)) {
-            INFO(("talk() media changed\n"));
-            stuffp->changed = jiffies;
-        }
-
-        /* now actually get the data */
-        while (sz--) {
-            if (-1 == mcdx_getval(stuffp, timeout, -1, bp++)) {
-                WARN(("talk() %02x timed out (data), %d tr%s left\n", 
-                        cmd[0], tries - 1, tries == 2 ? "y" : "ies"));
-                st = -1; break;
-            }
-            TRACE((TALK, "talk() got 0x%02x\n", *(bp - 1)));
-        }
-    }
-
-#if QUIET == 0
-    if (!tries && st == -1) INFO(("talk() giving up\n"));
-#endif
-
-    stuffp->lock = 0;
-    wake_up_interruptible(&stuffp->lockq);
-
-	TRACE((TALK, "talk() done with 0x%02x\n", st));
-    return st;
-}
-
-/* MODULE STUFF ***********************************************************/
-#ifdef MODULE
-
-int init_module(void)
-{
-	int i;
-	int drives = 0;
-
-	mcdx_init();
-	for (i = 0; i < MCDX_NDRIVES; i++)  {
-		if (mcdx_stuffp[i]) {
-		TRACE((INIT, "init_module() drive %d stuff @ %p\n",
-				i, mcdx_stuffp[i]));
-			drives++;
-		}
-	}
-
-    if (!drives) 
-		return -EIO;
-
-    return 0;
-}
-
-void cleanup_module(void)
-{
-    int i;
-
-	WARN(("cleanup_module called\n"));
-	
-    for (i = 0; i < MCDX_NDRIVES; i++) {
-		struct s_drive_stuff *stuffp;
-		stuffp = mcdx_stuffp[i];
-		if (!stuffp) continue;
-		release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE);
-		free_irq(stuffp->irq);
-		if (stuffp->toc) {
-			TRACE((MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc));
-			kfree(stuffp->toc);
-		}
-		TRACE((MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp));
-		mcdx_stuffp[i] = NULL;
-		kfree(stuffp);
-    }
-
-    if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) 
-        WARN(("cleanup() unregister_blkdev() failed\n"));
-    else INFO(("cleanup() succeeded\n"));
-}
-
-#endif MODULE
-
-/* Support functions ************************************************/
-
-#if MCDX_DEBUG
-void trace(int level, const char* fmt, ...)
-{
-	char s[255];
-	va_list args;
-	if (level < 1) return;
-	va_start(args, fmt);
-	if (sizeof(s) < vsprintf(s, fmt, args))
-		printk(MCDX ":: dprintf exeeds limit!!\n");
-	else printk(MCDX ":: %s", s);
-	va_end(args);
-}
-#endif
-
-void warn(const char* fmt, ...)
-{
-	char s[255];
-	va_list args;
-	va_start(args, fmt);
-	if (sizeof(s) < vsprintf(s, fmt, args))
-		printk(MCDX ":: dprintf exeeds limit!!\n");
-	else printk(MCDX ": %s", s);
-	va_end(args);
-}
-
-
-int mcdx_init(void)
-{
-	int drive;
-
-	INFO((": Version 1.0a "
-			"mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp\n"));
-
-	/* zero the pointer array */
-	for (drive = 0; drive < MCDX_NDRIVES; drive++)
-		mcdx_stuffp[drive] = NULL;
-
-	/* do the initialisation */
-	for (drive = 0; drive < MCDX_NDRIVES; drive++) { 
-		struct s_version version;
-		struct s_drive_stuff* stuffp;
-        int size;
-
-        size = sizeof(*stuffp);
-		
-		TRACE((INIT, "init() try drive %d\n", drive));
-
-		TRACE((MALLOC, "init() malloc %d bytes\n", size));
-		if (!(stuffp = kmalloc(size, GFP_KERNEL))) {
-			WARN(("init() malloc failed\n"));
-			break; 
-		}
-
-		TRACE((INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp));
-
-		/* zero the stuff */
-		memset(stuffp, 0, sizeof(*stuffp));
-
-		stuffp->present = 0;		/* this should be 0 already */
-		stuffp->toc = NULL;			/* this should be NULL already */
-		stuffp->changed = jiffies;
-
-		/* setup our irq and i/o addresses */
-		stuffp->irq = irq(mcdx_drive_map[drive]);
-		stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]);
-		stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
-		stuffp->wreg_hcon = stuffp->wreg_reset + 1;
-		stuffp->wreg_chn = stuffp->wreg_hcon + 1;
-
-		/* check if i/o addresses are available */
-		if (0 != check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
-            WARN(("%s=0x%3p,%d: "
-                    "Init failed. I/O ports (0x%3p..0x3p) already in use.\n"
-                    MCDX, 
-                    stuffp->wreg_data, stuffp->irq,
-                    stuffp->wreg_data, 
-                    stuffp->wreg_data + MCDX_IO_SIZE - 1));
-			TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
-            kfree(stuffp);
-			TRACE((INIT, "init() continue at next drive\n"));
-			continue; /* next drive */
-		}
-
-		TRACE((INIT, "init() i/o port is available at 0x%3p\n", stuffp->wreg_data));
-
-		TRACE((INIT, "init() hardware reset\n"));
-		mcdx_reset(stuffp, HARD, 1);
-
-		TRACE((INIT, "init() get version\n"));
-		if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
-			/* failed, next drive */
-            WARN(("%s=0x%3p,%d: Init failed. Can't get version.\n",
-                    MCDX,
-                    stuffp->wreg_data, stuffp->irq));
-			TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
-            kfree(stuffp);
-			TRACE((INIT, "init() continue at next drive\n"));
-			continue;
-		}
-
-		switch (version.code) {
-		case 'D': 
-                stuffp->readcmd = READDSPEED; 
-                stuffp->present = DOUBLE | DOOR | MULTI; 
-                break;
-		case 'F': 
-                stuffp->readcmd = READSSPEED; 
-                stuffp->present = SINGLE | DOOR | MULTI;
-                break;
-		case 'M': 
-                stuffp->readcmd = READSSPEED;
-                stuffp->present = SINGLE;
-                break;
-		default: 
-                stuffp->present = 0; break;
-		}
-
-        stuffp->playcmd = READSSPEED;
-
-
-		if (!stuffp->present) {
-            WARN(("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
-                    MCDX, stuffp->wreg_data, stuffp->irq));
-			kfree(stuffp);
-			continue; /* next drive */
-		}
-
-		TRACE((INIT, "init() register blkdev\n"));
-		if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) {
-            WARN(("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
-                    MCDX,
-                    stuffp->wreg_data, stuffp->irq, MAJOR_NR));
-			kfree(stuffp);
-			continue; /* next drive */
-		}
-
-		blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-		read_ahead[MAJOR_NR] = READ_AHEAD;
-
-#if WE_KNOW_WHY
-		 blksize_size[MAJOR_NR] = BLKSIZES;
-#endif
-
-		TRACE((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)) {
-            WARN(("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
-                    MCDX,
-                    stuffp->wreg_data, stuffp->irq, stuffp->irq));
-			stuffp->irq = 0;
-			kfree(stuffp);
-			continue;
-		}
-		request_region((unsigned int) stuffp->wreg_data, 
-                MCDX_IO_SIZE, 
-                DEVICE_NAME); 
-
-		TRACE((INIT, "init() get garbage\n"));
-		{
-			int i;
-			mcdx_delay(stuffp, HZ/2);
-			for (i = 100; i; i--) (void) inb((unsigned int) stuffp->rreg_status);
-		}
-
-
-#if WE_KNOW_WHY
-			outb(0x50, (unsigned int) stuffp->wreg_chn);	/* irq 11 -> channel register */
-#endif
-
-		TRACE((INIT, "init() set non dma but irq mode\n"));
-		mcdx_config(stuffp, 1);
-
-		stuffp->minor = drive;
-
-		WARN((DEVICE_NAME " installed at 0x%3p, irq %d."
-			   " (Firmware version %c %x)\n",
-			   stuffp->wreg_data, stuffp->irq, version.code,
-               version.ver));
-		mcdx_stuffp[drive] = stuffp;
-		TRACE((INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp));
-	}
-
-	return 0;
-}
-
-
-static int mcdx_transfer(struct s_drive_stuff *stuffp,
-		char *p, int sector, int nr_sectors)
-/*	This does actually the transfer from the drive.
-	Return:	-1 on timeout or other error
-			else status byte (as in stuff->st) */
-{
-    int off;
-    int done = 0;
-
-	TRACE((TRANSFER, "transfer() %d sectors at sector %d\n",
-			nr_sectors, sector));
-
-	if (stuffp->audio) {
-		WARN(("attempt to read from audio cd\n"));
-		return -1;
-	}
-
-    while (stuffp->lock)
-		interruptible_sleep_on(&stuffp->lockq);
-    if (current->signal && ~current->blocked) {
-        WARN(("talk() got signal %d\n", current->signal));
-    }
-
-    if (stuffp->valid
-			&& (sector >= stuffp->pending)
-			&& (sector < stuffp->off_direct)) {
-
-
-	off = stuffp->off_requested < (off = sector + nr_sectors)
-			? stuffp->off_requested : off;
-
-	stuffp->lock = current->pid;
-
-	do {
-	    int sig = 0;
-	    int to = 0;
-
-		/* wait for the drive become idle */
-	    current->timeout = jiffies + 5*HZ;
-	    while (stuffp->busy) {
-			interruptible_sleep_on(&stuffp->busyq);
-			if ((sig = (current->signal && ~current->blocked))
-					|| (to = (current->timeout == 0))) {
-				break;
-			} 	
-		}
-
-	    current->timeout = 0;
-
-		/* test for possible errors */
-	    if (((stuffp->busy == 0) && !stuffp->introk)
-				|| sig
-				|| to) {
-			if ((stuffp->busy == 0) && !stuffp->introk)
-                WARN(("mcdx_transfer() failure in data request\n"));
-			else if (to)
-                WARN(("mcdx_transfer(): timeout\n"));
-			else if (sig)
-                WARN(("mcdx_transfer(): got signal 0x%lx\n", current->signal));
-
-			stuffp->lock = 0;
-			stuffp->busy = 0;
-			wake_up_interruptible(&stuffp->lockq);
-			wake_up_interruptible(&stuffp->busyq);
-			stuffp->errno = MCDX_E;
-			TRACE((TRANSFER, "transfer() done (-1)\n"));
-			return -1;
-	    }
-
-		/* test if it's the first sector of a block,
-		 * there we have to skip some bytes as we read raw data */
-		if (stuffp->xa && (0 == (stuffp->pending & 3))) {
-			const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE;
-			TRACE((TRANSFER, "transfer() sector %d, skip %d header bytes\n",
-				stuffp->pending, HEAD));
-			insb((unsigned int) stuffp->rreg_data, p, HEAD);
-		}
-
-		/* now actually read the data */
-
-		TRACE((TRANSFER, "transfer() read sector %d\n", stuffp->pending));
-	    insb((unsigned int) stuffp->rreg_data, p, 512); 
-
-		/* test if it's the last sector of a block,
-		 * if so, we have to expect an interrupt and to skip some
-		 * data too */
-		if ((stuffp->busy = (3 == (stuffp->pending & 3))) && stuffp->xa) {
-			char dummy[CD_XA_TAIL];
-			TRACE((TRANSFER, "transfer() sector %d, skip %d trailer bytes\n",
-					stuffp->pending, CD_XA_TAIL));
-			insb((unsigned int) stuffp->rreg_data, &dummy[0], CD_XA_TAIL);
-		}
-
-	    if (stuffp->pending == sector) {
-			p += 512;
-			done++;
-			sector++;
-	    }
-	}
-	while (++(stuffp->pending) < off);
-
-	stuffp->lock = 0;
-	wake_up_interruptible(&stuffp->lockq);
-
-    } else {
-
-		static unsigned char cmd[] = {
-			0,
-			0, 0, 0,
-			0, 0, 0
-		};
-
-		cmd[0] = stuffp->readcmd;
-
-		stuffp->valid = 1;
-		stuffp->pending = sector & ~3;
-
-		/* do some sanity checks */
-		TRACE((TRANSFER, "transfer() request sector %d\n", stuffp->pending));
-		if (stuffp->pending > stuffp->lastsector) {
-			WARN(("transfer() sector %d from nirvana requested.\n",
-				stuffp->pending));
-			stuffp->errno = MCDX_EOM;
-			TRACE((TRANSFER, "transfer() done (-1)\n"));
-			return -1;
-		}
-
-		if ((stuffp->off_direct = stuffp->pending + DIRECT_SIZE)
-			> stuffp->lastsector + 1)
-			stuffp->off_direct = stuffp->lastsector + 1;
-		if ((stuffp->off_requested = stuffp->pending + REQUEST_SIZE)
-			> stuffp->lastsector + 1)
-			stuffp->off_requested = stuffp->lastsector + 1;
-
-		TRACE((TRANSFER, "transfer() pending %d\n", stuffp->pending));
-		TRACE((TRANSFER, "transfer() off_dir %d\n", stuffp->off_direct));
-		TRACE((TRANSFER, "transfer() off_req %d\n", stuffp->off_requested));
-
-		{
-			struct s_msf pending;
-			log2msf(stuffp->pending / 4, &pending);
-			cmd[1] = pending.minute;
-			cmd[2] = pending.second;
-			cmd[3] = pending.frame;
-		}
-
-		stuffp->busy = 1;
-		cmd[6] = (unsigned char) (stuffp->off_requested - stuffp->pending) / 4;
-
-		outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
-
-    }
-
-    stuffp->off_direct = (stuffp->off_direct += done) < stuffp->off_requested
-	    ? stuffp->off_direct : stuffp->off_requested;
-
-	TRACE((TRANSFER, "transfer() done (%d)\n", done));
-    return done;
-}
-
-
-/*	Access to elements of the mcdx_drive_map members */
-
-static char* port(int *ip) { return (char*) ip[0]; }
-static int irq(int *ip) { return ip[1]; }
-
-/*	Misc number converters */
-
-static unsigned int bcd2uint(unsigned char c)
-{ return (c >> 4) * 10 + (c & 0x0f); }
-
-static unsigned int uint2bcd(unsigned int ival)
-{ return ((ival / 10) << 4) | (ival % 10); }
-
-static void log2msf(unsigned int l, struct s_msf* pmsf)
-{
-    l += CD_BLOCK_OFFSET;
-    pmsf->minute = uint2bcd(l / 4500), l %= 4500;
-    pmsf->second = uint2bcd(l / 75);
-    pmsf->frame = uint2bcd(l % 75);
-}
-
-static unsigned int msf2log(const struct s_msf* pmsf)
-{
-    return bcd2uint(pmsf->frame)
-    + bcd2uint(pmsf->second) * 75
-    + bcd2uint(pmsf->minute) * 4500
-    - CD_BLOCK_OFFSET;
-}
-	
-int mcdx_readtoc(struct s_drive_stuff* stuffp)
-/*  Read the toc entries from the CD,
- *  Return: -1 on failure, else 0 */
-{
-
-	if (stuffp->toc) {
-		TRACE((IOCTL, "ioctl() toc already read\n"));
-		return 0;
-	}
-
-	TRACE((IOCTL, "ioctl() readtoc for %d tracks\n",
-			stuffp->di.n_last - stuffp->di.n_first + 1));
-
-    if (-1 == mcdx_hold(stuffp, 1)) return -1;
-
-	TRACE((IOCTL, "ioctl() tocmode\n"));
-	if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO;
-
-	/* all seems to be ok so far ... malloc */
-	{
-		int size;
-		size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2);
-
-		TRACE((MALLOC, "ioctl() malloc %d bytes\n", size));
-		stuffp->toc = kmalloc(size, GFP_KERNEL);
-		if (!stuffp->toc) {
-			WARN(("Cannot malloc %s bytes for toc\n", size));
-			mcdx_setdrivemode(stuffp, DATA, 1);
-			return -EIO;
-		}
-	}
-
-	/* now read actually the index */
-	{
-		int trk;
-		int retries;
-
-		for (trk = 0; 
-				trk < (stuffp->di.n_last - stuffp->di.n_first + 1); 
-				trk++)
-			stuffp->toc[trk].index = 0;
-
-		for (retries = 300; retries; retries--) { /* why 300? */
-			struct s_subqcode q;
-			unsigned int idx;
-		
-			if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) {
-				mcdx_setdrivemode(stuffp, DATA, 1);
-				return -EIO;
-			}
-
-			idx = bcd2uint(q.index);
-
-			if ((idx > 0) 
-					&& (idx <= stuffp->di.n_last) 
-					&& (q.tno == 0)
-					&& (stuffp->toc[idx - stuffp->di.n_first].index == 0)) {
-				stuffp->toc[idx - stuffp->di.n_first] = q;
-				TRACE((IOCTL, "ioctl() toc idx %d (trk %d)\n", idx, trk));
-				trk--;
-			}
-			if (trk == 0) break;
-		}
-		memset(&stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1], 
-				0, sizeof(stuffp->toc[0]));
-		stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt
-				= stuffp->di.msf_leadout;
-	}
-
-	/* unset toc mode */
-	TRACE((IOCTL, "ioctl() undo toc mode\n"));
-	if (-1 == mcdx_setdrivemode(stuffp, DATA, 2))
-		return -EIO;
-
-#if MCDX_DEBUG && IOCTL
-	{ int trk;
-	for (trk = 0; 
-			trk < (stuffp->di.n_last - stuffp->di.n_first + 2); 
-			trk++)
-		TRACE((IOCTL, "ioctl() %d readtoc %02x %02x %02x"
-				"  %02x:%02x.%02x  %02x:%02x.%02x\n",
-				trk + stuffp->di.n_first,
-				stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index,
-				stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame,
-				stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame));
-	}
-#endif
-
-	return 0;
-}
-
-static int
-mcdx_playmsf(struct s_drive_stuff* stuffp, const struct cdrom_msf* msf)
-{
-    unsigned char cmd[7] = {
-        0, 0, 0, 0, 0, 0, 0
-    };
-
-    cmd[0] = stuffp->playcmd;
-    
-    cmd[1] = msf->cdmsf_min0;
-    cmd[2] = msf->cdmsf_sec0;
-    cmd[3] = msf->cdmsf_frame0;
-    cmd[4] = msf->cdmsf_min1;
-    cmd[5] = msf->cdmsf_sec1;
-    cmd[6] = msf->cdmsf_frame1;
-
-    TRACE((IOCTL, "ioctl(): play %x "
-            "%02x:%02x:%02x -- %02x:%02x:%02x\n",
-            cmd[0], cmd[1], cmd[2], cmd[3],
-            cmd[4], cmd[5], cmd[6])); 
-
-    outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
-
-    if (-1 == mcdx_getval(stuffp, 1*HZ, 0, NULL)) {
-        WARN(("playmsf() timeout\n")); 
-        return -1;
-    }
-
-    stuffp->audiostatus = CDROM_AUDIO_PLAY;
-    return 0;
-}
-
-static int 
-mcdx_playtrk(struct s_drive_stuff* stuffp, const struct cdrom_ti* ti)
-{
-    struct s_subqcode* p;
-    struct cdrom_msf msf;
-
-    if (-1 == mcdx_readtoc(stuffp)) return -1;
-
-    if (ti) p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first];
-    else p = &stuffp->start;
-
-    msf.cdmsf_min0 = p->dt.minute;
-    msf.cdmsf_sec0 = p->dt.second;
-    msf.cdmsf_frame0 = p->dt.frame;
-
-    if (ti) {
-        p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1];
-        stuffp->stop = *p;
-    } else p = &stuffp->stop;
-
-    msf.cdmsf_min1 = p->dt.minute;
-    msf.cdmsf_sec1 = p->dt.second;
-    msf.cdmsf_frame1 = p->dt.frame;
-
-    return mcdx_playmsf(stuffp, &msf);
-}
-
-
-/* Drive functions ************************************************/
-
-static int 
-mcdx_closedoor(struct s_drive_stuff *stuffp, int tries)
-{
-	if (stuffp->present & DOOR)
-		return mcdx_talk(stuffp, "\xf8", 1, NULL, 0, 5*HZ, tries);
-	else
-		return 0;
-}
-
-static int 
-mcdx_stop(struct s_drive_stuff *stuffp, int tries)
-{ return mcdx_talk(stuffp, "\xf0", 1, NULL, 0, 2*HZ, tries); }
-
-static int
-mcdx_hold(struct s_drive_stuff *stuffp, int tries)
-{ return mcdx_talk(stuffp, "\x70", 1, NULL, 0, 2*HZ, tries); }
-
-static int
-mcdx_eject(struct s_drive_stuff *stuffp, int tries)
-{
-	if (stuffp->present & DOOR)
-		return mcdx_talk(stuffp, "\xf6", 1, NULL, 0, 5*HZ, tries);
-	else
-		return 0;
-}
-
-static int
-mcdx_requestsubqcode(struct s_drive_stuff *stuffp, 
-        struct s_subqcode *sub, 
-        int tries)
-{
-	char buf[11];
-	int ans;
-
-	if (-1 == (ans = mcdx_talk(
-            stuffp, "\x20", 1, buf, sizeof(buf),
-            2*HZ, tries))) 
-        return -1;
-	sub->control = buf[1];
-	sub->tno = buf[2];
-	sub->index = buf[3];
-	sub->tt.minute = buf[4];
-	sub->tt.second = buf[5];
-	sub->tt.frame = buf[6];
-	sub->dt.minute = buf[8];
-	sub->dt.second = buf[9];
-	sub->dt.frame = buf[10];
-
-	return ans;
-}
-
-static int
-mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, struct s_multi *multi, int tries)
-{
-	char buf[5];
-	int ans;
-
-    if (stuffp->present & MULTI) {
-        ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2*HZ, tries);
-        multi->multi = buf[1];
-        multi->msf_last.minute = buf[2];
-        multi->msf_last.second = buf[3];
-        multi->msf_last.frame = buf[4];
-        return ans;
-    } else {
-        multi->multi = 0;
-        return 0;
-    }
-}
-
-static int 
-mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, int tries)
-{
-	char buf[9];
-	int ans;
-	ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2*HZ, tries);
-	info->n_first = bcd2uint(buf[1]);
-	info->n_last = bcd2uint(buf[2]);
-	info->msf_leadout.minute = buf[3];
-	info->msf_leadout.second = buf[4];
-	info->msf_leadout.frame = buf[5];
-	info->msf_first.minute = buf[6];
-	info->msf_first.second = buf[7];
-	info->msf_first.frame = buf[8];
-	return ans;
-}
-
-static int
-mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries)
-{
-	char cmd[2];
-	int ans;
-
-	TRACE((HW, "setdrivemode() %d\n", mode));
-
-	if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5*HZ, tries)))
-		return -1;
-
-	switch (mode) {
-	  case TOC: cmd[1] |= 0x04; break;
-	  case DATA: cmd[1] &= ~0x04; break;
-	  case RAW: cmd[1] |= 0x40; break;
-	  case COOKED: cmd[1] &= ~0x40; break;
-	  default: break;
-	}
-	cmd[0] = 0x50;
-	return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries);
-}
-
-
-static int
-mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries)
-{
-	unsigned char cmd[2] = { 0xa0 };
-	TRACE((HW, "setdatamode() %d\n", mode));
-	switch (mode) {
-	  case MODE0: cmd[1] = 0x00; break;
-	  case MODE1: cmd[1] = 0x01; break;
-	  case MODE2: cmd[1] = 0x02; break;
-	  default: return -EINVAL;
-	}
-	return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries);
-}
-
-static int
-mcdx_config(struct s_drive_stuff *stuffp, int tries)
-{
-	char cmd[4];
-
-	TRACE((HW, "config()\n"));
-
-	cmd[0] = 0x90;
-
-	cmd[1] = 0x10;		/* irq enable */
-	cmd[2] = 0x05;		/* pre, err irq enable */
-
-	if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries))
-		return -1;
-
-	cmd[1] = 0x02;		/* dma select */
-	cmd[2] = 0x00;		/* no dma */
-
-	return mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries);
-}
-
-static int
-mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, int tries)
-{
-	char buf[3];
-	int ans;
-
-	if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 1, buf, sizeof(buf), 2*HZ, tries)))
-		return ans;
-
-	ver->code = buf[1];
-	ver->ver = buf[2];
-
-	return ans;
-}
-
-static int
-mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries)
-{ 
-	if (mode == HARD) {
-		outb(0, (unsigned int) stuffp->wreg_chn);		/* no dma, no irq -> hardware */
-		outb(0, (unsigned int) stuffp->wreg_reset);		/* hw reset */
-		return 0;
-	} else return mcdx_talk(stuffp, "\x60", 1, NULL, 0, 5*HZ, tries);
-}
-
-static int
-mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries)
-{
-	char cmd[2] = { 0xfe };
-    if (stuffp->present & DOOR) {
-        cmd[1] = lock ? 0x01 : 0x00;
-        return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 0, 5*HZ, tries);
-    } else 
-        return 0;
-}
-
-static int
-mcdx_getstatus(struct s_drive_stuff *stuffp, int tries)
-{ return mcdx_talk(stuffp, "\x40", 1, NULL, 0, 5*HZ, tries); }
-
-static int
-mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char* buf)
-{
-    unsigned long timeout = to + jiffies;
-    char c;
-
-    if (!buf) buf = &c;
-
-    while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) {
-        if (jiffies > timeout) return -1;
-        mcdx_delay(stuffp, delay);
-    }
-
-    *buf = (unsigned char) inb((unsigned int) stuffp->rreg_data) & 0xff;
-
-    return 0;
-}
diff -u --recursive --new-file v1.3.35/linux/drivers/block/optcd.c linux/drivers/block/optcd.c
--- v1.3.35/linux/drivers/block/optcd.c	Tue Oct 10 18:46:32 1995
+++ linux/drivers/block/optcd.c	Thu Jan  1 02:00:00 1970
@@ -1,1669 +0,0 @@
-/*	$Id: optcd.c,v 1.3 1995/08/24 19:54:27 root Exp root $
-	linux/drivers/block/optcd.c - Optics Storage 8000 AT CDROM driver
-
-	Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
-
-	Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks
-	by Eberhard Moenkeberg (emoenke@gwdg.de). ISP16 detection and
-	configuration by Eric van der Maarel (maarel@marin.nl), with some data
-	communicated by Vadim V. Model (vadim@rbrf.msk.su).
-
-	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; if not, write to the Free Software
-	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-	History
-	14-5-95		v0.0	Plays sound tracks. No reading of data CDs yet.
-				Detection of disk change doesn't work.
-	21-5-95		v0.1	First ALPHA version. CD can be mounted. The
-				device major nr is borrowed from the Aztech
-				driver. Speed is around 240 kb/s, as measured
-				with "time dd if=/dev/cdrom of=/dev/null \
-				bs=2048 count=4096".
-	24-6-95		v0.2	Reworked the #defines for the command codes
-				and the like, as well as the structure of
-				the hardware communication protocol, to
-				reflect the "official" documentation, kindly
-				supplied by C.K. Tan, Optics Storage Pte. Ltd.
-				Also tidied up the state machine somewhat.
-	28-6-95		v0.3	Removed the ISP-16 interface code, as this
-				should go into its own driver. The driver now
-				has its own major nr.
-				Disk change detection now seems to work, too.
-				This version became part of the standard
-				kernel as of version 1.3.7
-	24-9-95		v0.4	Re-inserted ISP-16 interface code which I
-				copied from sjcd.c, with a few changes.
-				Updated README.optcd. Submitted for
-				inclusion in 1.3.21
-	29-9-95		v0.4a	Fixed bug that prevented compilation as module
-*/
-
-#include <linux/major.h>
-#include <linux/config.h>
-
-#ifdef MODULE
-# include <linux/module.h>
-# include <linux/version.h>
-# ifndef CONFIG_MODVERSIONS
-	char kernel_version[]= UTS_RELEASE;
-# endif
-#define optcd_init init_module
-#else
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
-#endif
-
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/timer.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <asm/io.h>
-
-#define MAJOR_NR OPTICS_CDROM_MAJOR
-
-# include "blk.h"
-#define optcd_port optcd	/* Needed for the modutils. */
-# include <linux/optcd.h>
-
-
-/* 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 due to soundcard conflicts */
-#define ISP16_IRQ_7  0x08  /* shouldn't be used due to soundcard 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 drive */
-#define ISP16_NO_IDE__ENABLE_CDROM_PORT  0xF90  /* ISP16 without IDE interface */
-#define ISP16_IDE__ENABLE_CDROM_PORT  0xF91  /* ISP16 with IDE interface */
-#define ISP16_ENABLE_CDROM  0x80  /* seven bit */
-
-/* the magic stuff */
-#define ISP16_CTRL_PORT  0xF8F
-#define ISP16_NO_IDE__CTRL  0xE2  /* ISP16 without IDE interface */
-#define ISP16_IDE__CTRL  0xE3  /* ISP16 with IDE interface */
-
-static short isp16_detect(void);
-static short isp16_no_ide__detect(void);
-static short isp16_with_ide__detect(void);
-static short isp16_config( int base, u_char drive_type, int irq, int dma );
-static short isp16_type; /* dependent on type of interface card */
-static u_char isp16_ctrl;
-static u_short isp16_enable_cdrom_port;
-
-
-static short optcd_port = OPTCD_PORTBASE;
-
-/* Read current status/data availability flags */
-inline static int optFlags(void) {
-	return inb(STATUS_PORT) & FL_STDT;
-}
-
-/* Wait for status available; return TRUE on timeout */
-static int sten_low(void) {
-	int no_status;
-	unsigned long count = 0;
-	while ((no_status = (optFlags() & FL_STEN)))
-		if (++count >= BUSY_TIMEOUT)
-			break;
-#ifdef DEBUG_DRIVE_IF
-	if (no_status)
-		printk("optcd: timeout waiting for STEN low\n");
-	else
-		printk("optcd: STEN low after %ld\n", count);
-#endif
-	return no_status;
-}
-
-/* Wait for data available; return TRUE on timeout */
-static int dten_low(void) {
-	int no_data;
-	unsigned long count = 0;
-	while ((no_data = (optFlags() & FL_DTEN)))
-		if (++count >= BUSY_TIMEOUT)
-			break;
-#ifdef DEBUG_DRIVE_IF
-	if (no_data)
-		printk("optcd: timeout waiting for DTEN low\n");
-	else
-		printk("optcd: DTEN low after %ld\n", count);
-#endif
-	return no_data;
-}
-
-/* Facilities for polled waiting for status or data */
-static int sleep_timeout;		/* Max amount of time still to sleep */
-static unsigned char sleep_flags;	/* Flags read last time around */
-static struct wait_queue *waitq = NULL;
-static struct timer_list delay_timer = {NULL, NULL, 0, 0, NULL};
-
-/* Timer routine: wake up when either of FL_STEN or FL_DTEN goes down,
- * or when timeout expires. Otherwise wait some more.
- */
-static void sleep_timer(void) {
-	if ((sleep_flags = optFlags()) != FL_STDT) {
-		wake_up(&waitq);
-		return;
-	}
-	if (--sleep_timeout <= 0) {
-		wake_up(&waitq);
-		return;
-	}
-	SET_TIMER(sleep_timer, 1);
-}
-
-/* Sleep until any of FL_STEN or FL_DTEN go down, or until timeout.
- * sleep_timeout must be set first.
- */
-static int sleep_status(void) {
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: sleeping %d on status\n", sleep_timeout);
-#endif
-	if (sleep_timeout <= 0)		/* timeout immediately */
-		return FL_STDT;
-	if ((sleep_flags = optFlags()) == FL_STDT) {
-		SET_TIMER(sleep_timer, 1);
-		sleep_on(&waitq);
-	}
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: woken up with %d to go, flags %d\n",
-		sleep_timeout, sleep_flags);
-#endif
-	return sleep_flags;
-}
-
-/* Sleep until status available; return TRUE on timeout */
-inline static int sleep_sten_low(void) {
-	int flags;
-	sleep_timeout = SLEEP_TIMEOUT;
-	flags = sleep_status();
-#ifdef DEBUG_DRIVE_IF
-	if (!(flags & FL_DTEN))
-		printk("optcd: DTEN while waiting for STEN\n");
-#endif
-	return flags & FL_STEN;
-}
-
-/* Sleep until data available; return TRUE on timeout */
-inline static int sleep_dten_low(void) {
-	int flags;
-	sleep_timeout = SLEEP_TIMEOUT;
-	flags = sleep_status();
-#ifdef DEBUG_DRIVE_IF
-	if (!(flags & FL_STEN))
-		printk("optcd: STEN while waiting for DTEN\n");
-#endif
-	return flags & FL_DTEN;
-}
-
-/* Send command code. Return <0 indicates error */
-static int optSendCmd(int cmd) {
-	unsigned char ack;
-#if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
-	printk("optcd: executing command 0x%02x\n", cmd);
-#endif
-	outb(HCON_DTS, HCON_PORT);	/* Enable Suspend Data Transfer */
-	outb(cmd, COMIN_PORT);		/* Send command code */
-	if (sten_low())			/* Wait for status available */
-		return -ERR_IF_CMD_TIMEOUT;
-	ack = inb(DATA_PORT);		/* read command acknowledge */
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: acknowledge code 0x%02x\n", ack);
-#endif
-	outb(HCON_SDRQB, HCON_PORT);	/* Disable Suspend Data Transfer */
-	return ack==ST_OP_OK ? 0 : -ack;
-}
-
-/* Send command parameters. Return <0 indicates error */
-static int optSendParams(struct opt_Play_msf *params) {
-	unsigned char ack;
-#if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
-	printk("optcd: params %02x:%02x:%02x %02x:%02x:%02x\n",
-		params->start.min, params->start.sec, params->start.frame,
-		params->end.min, params->end.sec, params->end.frame);
-#endif
-	outb(params -> start.min, COMIN_PORT);
-	outb(params -> start.sec, COMIN_PORT);
-	outb(params -> start.frame, COMIN_PORT);
-	outb(params -> end.min, COMIN_PORT);
-	outb(params -> end.sec, COMIN_PORT);
-	outb(params -> end.frame, COMIN_PORT);
-	if (sten_low())			/* Wait for status available */
-		return -ERR_IF_CMD_TIMEOUT;
-	ack = inb(DATA_PORT);		/* read command acknowledge */
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: acknowledge code 0x%02x\n", ack);
-#endif
-	return ack==ST_PA_OK ? 0 : -ack;
-}
-
-/* Return execution status for quick response commands, i.e. busy wait.
- * Return value <0 indicates timeout.
- */
-static int optGetExecStatus(void) {
-	unsigned char exec_status;
-	if (sten_low())			/* Wait for status available */
-		return -ERR_IF_CMD_TIMEOUT;
-	exec_status = inb(DATA_PORT);	/* read command execution status */
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: returned execution status: 0x%02x\n", exec_status);
-#endif
-	return exec_status;
-}
-
-/* Return execution status for slow commands. Only use when no data is
- * expected. Return value <0 indicates timeout.
- */
-static int optSleepTillExecStatus(void) {
-	unsigned char exec_status;
-	if (sleep_sten_low())		/* Wait for status available */
-		return -ERR_IF_CMD_TIMEOUT;
-	exec_status = inb(DATA_PORT);	/* read command execution status */
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: returned execution status: 0x%02x\n", exec_status);
-#endif
-	return exec_status;
-}
-
-/* Fetch status that has previously been waited for. <0 means not available */
-inline static int optStatus(void) {
-	unsigned char status;
-	if (optFlags() & FL_STEN)
-		return -ERR_IF_NOSTAT;
-	status = inb(DATA_PORT);
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: read status: 0x%02x\n", status);
-#endif
-	return status;
-}
-
-/* Wait for extra byte of data that a command returns */
-static int optGetData(void) {
-	unsigned char data;
-	if (sten_low())
-		return -ERR_IF_DATA_TIMEOUT;
-	data = inb(DATA_PORT);
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: read data: 0x%02x\n", data);
-#endif
-	return data;
-}
-
-/* Read data that has previously been waited for. */
-inline static void optReadData(char *buf, int n) {
-	insb(DATA_PORT, buf, n);
-}
-
-/* Flush status and data fifos */
-inline static void optFlushData(void) {
-	while (optFlags() != FL_STDT)
-		inb(DATA_PORT);
-}
-
-/* Write something to RESET_PORT and wait. Return TRUE upon success. */
-static int optResetDrive(void) {
-	unsigned long count = 0;
-	int flags;
-#ifdef DEBUG_DRIVE_IF
-	printk("optcd: reset drive\n");
-#endif
-	outb(0, RESET_PORT);
-	while (++count < RESET_WAIT)
-		inb(DATA_PORT);
-	count = 0;
-	while ((flags = (inb(STATUS_PORT) & FL_RESET)) != FL_RESET)
-		if (++count >= BUSY_TIMEOUT)
-			break;
-#ifdef DEBUG_DRIVE_IF
-	if (flags == FL_RESET)
-		printk("optcd: drive reset\n");
-	else
-		printk("optcd: reset failed\n");
-#endif
-	if (flags != FL_RESET)
-		return 0;		/* Reset failed */
-	outb(HCON_SDRQB, HCON_PORT);	/* Disable Suspend Data Transfer */
-	return 1;			/* Reset succeeded */
-}
-
-
-/* Command protocol */
-
-/* Send a simple command and wait for response */
-inline static int optCmd(int cmd) {
-	int ack = optSendCmd(cmd);
-	if (ack < 0)
-		return ack;
-	if (cmd < COMFETCH)		/* Quick response command */
-		return optGetExecStatus();
-	else				/* Slow command */
-		return optSleepTillExecStatus();
-}
-
-/* Send a command with parameters and wait for response */
-inline static int optPlayCmd(int cmd, struct opt_Play_msf *params) {
-	int ack = optSendCmd(cmd);
-	if (ack < 0)
-		return ack;
-	if ((ack = optSendParams(params)) < 0)
-		return ack;
-	return optSleepTillExecStatus();
-}
-
-/* Send a command with parameters. Don't wait for the response,
- * which consists of the data blocks read. */
-inline static int optReadCmd(int cmd, struct opt_Play_msf *params) {
-	int ack = optSendCmd(cmd);
-	if (ack < 0)
-		return ack;
-	return optSendParams(params);
-}
-
-
-/* Address conversion routines */
-
-/* Binary to BCD (2 digits) */
-inline static unsigned char bin2bcd(unsigned char p) {
-#ifdef DEBUG_CONV
-	if (p > 99)
-		printk("optcd: error bin2bcd %d\n", p);
-#endif
-	return (p % 10) | ((p / 10) << 4);
-}
-
-/* Linear address to minute, second, frame form */
-static void hsg2msf(long hsg, struct msf *msf) {
-	hsg += 150;
-	msf -> min = hsg / 4500;
-	hsg %= 4500;
-	msf -> sec = hsg / 75;
-	msf -> frame = hsg % 75;
-#ifdef DEBUG_CONV
-	if (msf -> min >= 70)
-		printk("optcd: Error hsg2msf address Minutes\n");
-	if (msf -> sec >= 60)
-		printk("optcd: Error hsg2msf address Seconds\n");
-	if (msf -> frame >= 75)
-		printk("optcd: Error hsg2msf address Frames\n");
-#endif
-	msf -> min = bin2bcd(msf -> min);	/* convert to BCD */
-	msf -> sec = bin2bcd(msf -> sec);
-	msf -> frame = bin2bcd(msf -> frame);
-}
-
-/* Two BCD digits to binary */
-inline static int bcd2bin(unsigned char bcd) {
-	return (bcd >> 4) * 10 + (bcd & 0x0f);
-}
-
-/* Minute, second, frame address to linear address */
-static long msf2hsg(struct msf *mp) {
-#ifdef DEBUG_CONV
-	if (mp -> min >= 70)
-		printk("optcd: Error msf2hsg address Minutes\n");
-	if (mp -> sec >= 60)
-		printk("optcd: Error msf2hsg address Seconds\n");
-	if (mp -> frame >= 75)
-		printk("optcd: Error msf2hsg address Frames\n");
-#endif
-	return bcd2bin(mp -> frame)
-		+ bcd2bin(mp -> sec) * 75
-		+ bcd2bin(mp -> min) * 4500
-		- 150;
-}
-
-
-/* Drive status and table of contents */
-
-static int optAudioStatus = CDROM_AUDIO_NO_STATUS;
-static char optDiskChanged = 1;
-static char optTocUpToDate = 0;
-static struct opt_DiskInfo DiskInfo;
-static struct opt_Toc Toc[MAX_TRACKS];
-
-/* Get CDROM status, flagging completion of audio play and disk changes. */
-static int optGetStatus(void) {
-	int st;
-	if ((st = optCmd(COMIOCTLISTAT)) < 0)
-		return st;
-	if (st == 0xff)
-		return -ERR_IF_NOSTAT;
-	if (((st & ST_MODE_BITS) != ST_M_AUDIO) &&
-		(optAudioStatus == CDROM_AUDIO_PLAY)) {
-		optAudioStatus = CDROM_AUDIO_COMPLETED;
-	}
-	if (st & ST_DSK_CHG) {
-		optDiskChanged = 1;
-		optTocUpToDate = 0;
-		optAudioStatus = CDROM_AUDIO_NO_STATUS;
-	}
-	return st;
-}
-
-/*
- * Read the current Q-channel info. Also used for reading the
- * table of contents.
- */
-static int optGetQChannelInfo(struct opt_Toc *qp) {
-	int st;
-#ifdef DEBUG_TOC
-	printk("optcd: starting optGetQChannelInfo\n");
-#endif
-	if ((st = optGetStatus()) < 0)
-		return st;
-	if ((st = optCmd(COMSUBQ)) < 0)
-		return st;
-	if ((qp -> ctrl_addr = st = optGetData()), st < 0) return st;
-	if ((qp -> track = st = optGetData()), st < 0) return st;
-	if ((qp -> pointIndex = st = optGetData()), st < 0) return st;
-	if ((qp -> trackTime.min = st = optGetData()), st < 0) return st;
-	if ((qp -> trackTime.sec = st = optGetData()), st < 0) return st;
-	if ((qp -> trackTime.frame = st = optGetData()), st < 0) return st;
-	if ((st = optGetData()) < 0) return st;		/* byte not used */
-	if ((qp -> diskTime.min = st = optGetData()), st < 0) return st;
-	if ((qp -> diskTime.sec = st = optGetData()), st < 0) return st;
-	if ((qp -> diskTime.frame = st = optGetData()), st < 0) return st;
-#ifdef DEBUG_TOC
-	printk("optcd: exiting optGetQChannelInfo\n");
-#endif
-	return 0;
-}
-
-#define QINFO_FIRSTTRACK	0xa0
-#define QINFO_LASTTRACK		0xa1
-#define QINFO_DISKLENGTH	0xa2
-
-static int optGetDiskInfo(void) {
-	int st, limit;
-	unsigned char test = 0;
-	struct opt_Toc qInfo;
-#ifdef DEBUG_TOC
-	printk("optcd: starting optGetDiskInfo\n");
-#endif
-	optDiskChanged = 0;
-	if ((st = optCmd(COMLEADIN)) < 0)
-		return st;
-	for (limit = 300; (limit > 0) && (test != 0x0f); limit--) {
-		if ((st = optGetQChannelInfo(&qInfo)) < 0)
-			return st;
-		switch (qInfo.pointIndex) {
-		case QINFO_FIRSTTRACK:
-			DiskInfo.first = bcd2bin(qInfo.diskTime.min);
-#ifdef DEBUG_TOC
-			printk("optcd: got first: %d\n", DiskInfo.first);
-#endif
-			test |= 0x01;
-			break;
-		case QINFO_LASTTRACK:
-			DiskInfo.last = bcd2bin(qInfo.diskTime.min);
-#ifdef DEBUG_TOC
-			printk("optcd: got last: %d\n", DiskInfo.last);
-#endif
-			test |= 0x02;
-			break;
-		case QINFO_DISKLENGTH:
-			DiskInfo.diskLength.min = qInfo.diskTime.min;
-			DiskInfo.diskLength.sec = qInfo.diskTime.sec-2;
-			DiskInfo.diskLength.frame = qInfo.diskTime.frame;
-#ifdef DEBUG_TOC
-			printk("optcd: got length: %x:%x.%x\n",
-				DiskInfo.diskLength.min,
-				DiskInfo.diskLength.sec,
-				DiskInfo.diskLength.frame);
-#endif
-			test |= 0x04;
-			break;
-		default:
-			if ((test & 0x01)	/* Got no of first track */
-			 && (qInfo.pointIndex == DiskInfo.first)) {
-				/* StartTime of First Track */
-				DiskInfo.firstTrack.min = qInfo.diskTime.min;
-				DiskInfo.firstTrack.sec = qInfo.diskTime.sec;
-				DiskInfo.firstTrack.frame = qInfo.diskTime.frame;
-#ifdef DEBUG_TOC
-			printk("optcd: got start: %x:%x.%x\n",
-				DiskInfo.firstTrack.min,
-				DiskInfo.firstTrack.sec,
-				DiskInfo.firstTrack.frame);
-#endif
-				test |= 0x08;
-			}
-		}
-	}
-#ifdef DEBUG_TOC
-	printk("optcd: exiting optGetDiskInfo\n");
-#endif
-	if (test != 0x0f)
-		return -ERR_TOC_MISSINGINFO;
-	return 0;
-}
-
-static int optGetToc(void) {	/* Presumes we have got DiskInfo */
-	int st, count, px, limit;
-	struct opt_Toc qInfo;
-#ifdef DEBUG_TOC
-	int i;
-	printk("optcd: starting optGetToc\n");
-#endif
-	for (count = 0; count < MAX_TRACKS; count++)
-		Toc[count].pointIndex = 0;
-	if ((st = optCmd(COMLEADIN)) < 0)
-		return st;
-	st = 0;
-	count = DiskInfo.last + 3;
-	for (limit = 300; (limit > 0) && (count > 0); limit--) {
-		if ((st = optGetQChannelInfo(&qInfo)) < 0)
-			break;
-		px = bcd2bin(qInfo.pointIndex);
-		if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
-			if (Toc[px].pointIndex == 0) {
-				Toc[px] = qInfo;
-				count--;
-			}
-	}
-	Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-#ifdef DEBUG_TOC
-	printk("optcd: exiting optGetToc\n");
-	for (i = 1; i <= DiskInfo.last + 1; i++)
-		printk("i = %3d ctl-adr = %02x track %2d px "
-			"%02x %02x:%02x.%02x %02x:%02x.%02x\n",
-			i, Toc[i].ctrl_addr,
-			Toc[i].track,
-			Toc[i].pointIndex,
-			Toc[i].trackTime.min,
-			Toc[i].trackTime.sec,
-			Toc[i].trackTime.frame,
-			Toc[i].diskTime.min,
-			Toc[i].diskTime.sec,
-			Toc[i].diskTime.frame);
-	for (i = 100; i < 103; i++)
-		printk("i = %3d ctl-adr = %02x track %2d px "
-			"%02x %02x:%02x.%02x %02x:%02x.%02x\n",
-			i, Toc[i].ctrl_addr,
-			Toc[i].track,
-			Toc[i].pointIndex,
-			Toc[i].trackTime.min,
-			Toc[i].trackTime.sec,
-			Toc[i].trackTime.frame,
-			Toc[i].diskTime.min,
-			Toc[i].diskTime.sec,
-			Toc[i].diskTime.frame);
-#endif
-	return count ? -ERR_TOC_MISSINGENTRY : 0;
-}
-
-static int optUpdateToc(void) {
-#ifdef DEBUG_TOC
-	printk("optcd: starting optUpdateToc\n");
-#endif
-	if (optTocUpToDate)
-		return 0;
-	if (optGetDiskInfo() < 0)
-		return -EIO;
-	if (optGetToc() < 0)
-		return -EIO;
-	optTocUpToDate = 1;
-#ifdef DEBUG_TOC
-	printk("optcd: exiting optUpdateToc\n");
-#endif
-	return 0;
-}
-
-
-/* Buffers */
-
-#define OPT_BUF_SIZ		16
-#define OPT_BLOCKSIZE		2048
-#define OPT_BLOCKSIZE_RAW	2336
-#define OPT_BLOCKSIZE_ALL	2646
-#define OPT_NOBUF		-1
-
-/* Buffer for block size conversion. */
-static char opt_buf[OPT_BLOCKSIZE*OPT_BUF_SIZ];
-static volatile int opt_buf_bn[OPT_BUF_SIZ], opt_next_bn;
-static volatile int opt_buf_in = 0, opt_buf_out = OPT_NOBUF;
-
-inline static void opt_invalidate_buffers(void) {
-	int i;
-#ifdef DEBUG_BUFFERS
-	printk("optcd: executing opt_invalidate_buffers\n");
-#endif
-	for (i = 0; i < OPT_BUF_SIZ; i++)
-		opt_buf_bn[i] = OPT_NOBUF;
-	opt_buf_out = OPT_NOBUF;
-}
-
-/*
- * Take care of the different block sizes between cdrom and Linux.
- * When Linux gets variable block sizes this will probably go away.
- */
-static void opt_transfer(void) {
-#if (defined DEBUG_BUFFERS) || (defined DEBUG_REQUEST)
-	printk("optcd: executing opt_transfer\n");
-#endif
-	if (!CURRENT_VALID)
-		return;
-	while (CURRENT -> nr_sectors) {
-		int bn = CURRENT -> sector / 4;
-		int i, offs, nr_sectors;
-		for (i = 0; i < OPT_BUF_SIZ && opt_buf_bn[i] != bn; ++i);
-#ifdef DEBUG_REQUEST
-		printk("optcd: found %d\n", i);
-#endif
-		if (i >= OPT_BUF_SIZ) {
-			opt_buf_out = OPT_NOBUF;
-			break;
-		}
-		offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
-		nr_sectors = 4 - (CURRENT -> sector & 3);
-		if (opt_buf_out != i) {
-			opt_buf_out = i;
-			if (opt_buf_bn[i] != bn) {
-				opt_buf_out = OPT_NOBUF;
-				continue;
-			}
-		}
-		if (nr_sectors > CURRENT -> nr_sectors)
-			nr_sectors = CURRENT -> nr_sectors;
-		memcpy(CURRENT -> buffer, opt_buf + offs, nr_sectors * 512);
-		CURRENT -> nr_sectors -= nr_sectors;
-		CURRENT -> sector += nr_sectors;
-		CURRENT -> buffer += nr_sectors * 512;
-	}
-}
-
-
-/* State machine for reading disk blocks */
-
-enum opt_state_e {
-	OPT_S_IDLE,	/* 0 */
-	OPT_S_START,	/* 1 */
-	OPT_S_READ,	/* 2 */
-	OPT_S_DATA,	/* 3 */
-	OPT_S_STOP,	/* 4 */
-	OPT_S_STOPPING	/* 5 */
-};
-
-static volatile enum opt_state_e opt_state = OPT_S_IDLE;
-#ifdef DEBUG_STATE
-static volatile enum opt_state_e opt_state_old = OPT_S_STOP;
-static volatile int opt_st_old = 0;
-static volatile long opt_state_n = 0;
-#endif
-
-static volatile int opt_transfer_is_active = 0;
-static volatile int opt_error = 0;	/* do something with this?? */
-static int optTries;			/* ibid?? */
-
-static void opt_poll(void) {
-	static int optTimeout;
-	static volatile int opt_read_count = 1;
-	int st = 0;
-	int loop_ctl = 1;
-	int skip = 0;
-
-	if (opt_error) {
-		printk("optcd: I/O error 0x%02x\n", opt_error);
-		opt_invalidate_buffers();
-#ifdef WARN_IF_READ_FAILURE
-		if (optTries == 5)
-			printk("optcd: read block %d failed; audio disk?\n",
-			        opt_next_bn);
-#endif
-		if (!optTries--) {
-			printk("optcd: read block %d failed; Giving up\n",
-			       opt_next_bn);
-			if (opt_transfer_is_active) {
-				optTries = 0;
-				loop_ctl = 0;
-			}
-			if (CURRENT_VALID)
-				end_request(0);
-			optTries = 5;
-		}
-		opt_error = 0;
-		opt_state = OPT_S_STOP;
-	}
-
-	while (loop_ctl)
-	{
-		loop_ctl = 0; /* each case must flip this back to 1 if we want
-		                 to come back up here */
-#ifdef DEBUG_STATE
-		if (opt_state == opt_state_old)
-			opt_state_n++;
-		else {
-			opt_state_old = opt_state;
-			if (++opt_state_n > 1)
-				printk("optcd: %ld times in previous state\n",
-					opt_state_n);
-			printk("optcd: state %d\n", opt_state);
-			opt_state_n = 0;
-		}
-#endif
-		switch (opt_state) {
-		case OPT_S_IDLE:
-			return;
-		case OPT_S_START:
-			if (optSendCmd(COMDRVST))
-				return;
-			opt_state = OPT_S_READ;
-			optTimeout = 3000;
-			break;
-		case OPT_S_READ: {
-			struct opt_Play_msf msf;
-			if (!skip) {
-				if ((st = optStatus()) < 0)
-					break;
-				if (st & ST_DSK_CHG) {
-					optDiskChanged = 1;
-					optTocUpToDate = 0;
-					opt_invalidate_buffers();
-				}
-			}
-			skip = 0;
-			if ((st & ST_DOOR_OPEN) || (st & ST_DRVERR)) {
-				optDiskChanged = 1;
-				optTocUpToDate = 0;
-				printk((st & ST_DOOR_OPEN)
-				       ? "optcd: door open\n"
-				       : "optcd: disk removed\n");
-				if (opt_transfer_is_active) {
-					opt_state = OPT_S_START;
-					loop_ctl = 1;
-					break;
-				}
-				opt_state = OPT_S_IDLE;
-				while (CURRENT_VALID)
-					end_request(0);
-				return;
-			}
-			if (!CURRENT_VALID) {
-				opt_state = OPT_S_STOP;
-				loop_ctl = 1;
-				break;
-			}
-			opt_next_bn = CURRENT -> sector / 4;
-			hsg2msf(opt_next_bn, &msf.start);
-			opt_read_count = OPT_BUF_SIZ;
-			msf.end.min = 0;
-			msf.end.sec = 0;
-			msf.end.frame = opt_read_count;
-#ifdef DEBUG_REQUEST
-			printk("optcd: reading %x:%x.%x %x:%x.%x\n",
-				msf.start.min,
-				msf.start.sec,
-				msf.start.frame,
-				msf.end.min,
-				msf.end.sec,
-				msf.end.frame);
-			printk("optcd: opt_next_bn:%d opt_buf_in:%d opt_buf_out:%d opt_buf_bn:%d\n",
-				opt_next_bn,
-				opt_buf_in,
-				opt_buf_out,
-				opt_buf_bn[opt_buf_in]);
-#endif
-			optReadCmd(COMREAD, &msf);
-			opt_state = OPT_S_DATA;
-			optTimeout = READ_TIMEOUT;
-			break;
-		}
-		case OPT_S_DATA:
-			st = optFlags() & (FL_STEN|FL_DTEN);
-#ifdef DEBUG_STATE
-			if (st != opt_st_old) {
-				opt_st_old = st;
-				printk("optcd: st:%x\n", st);
-			}
-			if (st == FL_STEN)
-				printk("timeout cnt: %d\n", optTimeout);
-#endif
-			switch (st) {
-			case FL_DTEN:
-#ifdef WARN_IF_READ_FAILURE
-				if (optTries == 5)
-					printk("optcd: read block %d failed; audio disk?\n",
-					       opt_next_bn);
-#endif
-				if (!optTries--) {
-					printk("optcd: read block %d failed; Giving up\n",
-					       opt_next_bn);
-					if (opt_transfer_is_active) {
-						optTries = 0;
-						break;
-					}
-					if (CURRENT_VALID)
-						end_request(0);
-					optTries = 5;
-				}
-				opt_state = OPT_S_START;
-				optTimeout = READ_TIMEOUT;
-				loop_ctl = 1;
-			case (FL_STEN|FL_DTEN):
-				break;
-			default:
-				optTries = 5;
-				if (!CURRENT_VALID && opt_buf_in == opt_buf_out) {
-					opt_state = OPT_S_STOP;
-					loop_ctl = 1;
-					break;
-				}
-				if (opt_read_count<=0)
-					printk("optcd: warning - try to read 0 frames\n");
-				while (opt_read_count) {
-					opt_buf_bn[opt_buf_in] = OPT_NOBUF;
-					if (dten_low()) { /* should be no waiting here!?? */
-						printk("read_count:%d CURRENT->nr_sectors:%ld opt_buf_in:%d\n",
-							opt_read_count,
-							CURRENT->nr_sectors,
-							opt_buf_in);
-						printk("opt_transfer_is_active:%x\n",
-							opt_transfer_is_active);
-						opt_read_count = 0;
-						opt_state = OPT_S_STOP;
-						loop_ctl = 1;
-						end_request(0);
-						break;
-					}
-					optReadData(opt_buf+OPT_BLOCKSIZE*opt_buf_in, OPT_BLOCKSIZE);
-					opt_read_count--;
-#ifdef DEBUG_REQUEST
-					printk("OPT_S_DATA; ---I've read data- read_count: %d\n",
-					       opt_read_count);
-					printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
-					       opt_next_bn,
-					       opt_buf_in,
-					       opt_buf_out,
-					       opt_buf_bn[opt_buf_in]);
-#endif
-					opt_buf_bn[opt_buf_in] = opt_next_bn++;
-					if (opt_buf_out == OPT_NOBUF)
-						opt_buf_out = opt_buf_in;
-					opt_buf_in = opt_buf_in + 1 ==
-						OPT_BUF_SIZ ? 0 : opt_buf_in + 1;
-				}
-				if (!opt_transfer_is_active) {
-					while (CURRENT_VALID) {
-						opt_transfer();
-						if (CURRENT -> nr_sectors == 0)
-							end_request(1);
-						else
-							break;
-					}
-				}
-
-				if (CURRENT_VALID
-				    && (CURRENT -> sector / 4 < opt_next_bn ||
-				    CURRENT -> sector / 4 >
-				     opt_next_bn + OPT_BUF_SIZ)) {
-					opt_state = OPT_S_STOP;
-					loop_ctl = 1;
-					break;
-				}
-				optTimeout = READ_TIMEOUT;
-				if (opt_read_count == 0) {
-					opt_state = OPT_S_STOP;
-					loop_ctl = 1;
-					break;
-				}
-			}
-			break;
-		case OPT_S_STOP:
-			if (opt_read_count != 0)
-				printk("optcd: discard data=%x frames\n",
-					opt_read_count);
-			while (opt_read_count != 0) {
-				optFlushData();
-				opt_read_count--;
-			}
-			if (optSendCmd(COMDRVST))
-				return;
-			opt_state = OPT_S_STOPPING;
-			optTimeout = 1000;
-			break;
-		case OPT_S_STOPPING:
-			if ((st = optStatus()) < 0 && optTimeout)
-					break;
-			if ((st != -1) && (st & ST_DSK_CHG)) {
-				optDiskChanged = 1;
-				optTocUpToDate = 0;
-				opt_invalidate_buffers();
-			}
-			if (CURRENT_VALID) {
-				if (st != -1) {
-					opt_state = OPT_S_READ;
-					loop_ctl = 1;
-					skip = 1;
-					break;
-				} else {
-					opt_state = OPT_S_START;
-					optTimeout = 1;
-				}
-			} else {
-				opt_state = OPT_S_IDLE;
-				return;
-			}
-			break;
-		default:
-			printk("optcd: invalid state %d\n", opt_state);
-			return;
-		} /* case */
-	} /* while */
-
-	if (!optTimeout--) {
-		printk("optcd: timeout in state %d\n", opt_state);
-		opt_state = OPT_S_STOP;
-		if (optCmd(COMSTOP) < 0)
-			return;
-	}
-
-	SET_TIMER(opt_poll, 1);
-}
-
-
-static void do_optcd_request(void) {
-#ifdef DEBUG_REQUEST
-	printk("optcd: do_optcd_request(%ld+%ld)\n",
-	       CURRENT -> sector, CURRENT -> nr_sectors);
-#endif
-	opt_transfer_is_active = 1;
-	while (CURRENT_VALID) {
-		if (CURRENT->bh) {
-			if (!CURRENT->bh->b_lock)
-				panic(DEVICE_NAME ": block not locked");
-		}
-		opt_transfer();	/* First try to transfer block from buffers */
-		if (CURRENT -> nr_sectors == 0) {
-			end_request(1);
-		} else {	/* Want to read a block not in buffer */
-			opt_buf_out = OPT_NOBUF;
-			if (opt_state == OPT_S_IDLE) {
-				/* Should this block the request queue?? */
-				if (optUpdateToc() < 0) {
-					while (CURRENT_VALID)
-						end_request(0);
-					break;
-				}
-				/* Start state machine */
-				opt_state = OPT_S_START;
-				optTries = 5;
-				SET_TIMER(opt_poll, 1);	/* why not start right away?? */
-			}
-			break;
-		}
-	}
-	opt_transfer_is_active = 0;
-#ifdef DEBUG_REQUEST
-	printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
-	       opt_next_bn, opt_buf_in, opt_buf_out, opt_buf_bn[opt_buf_in]);
-	printk("optcd: do_optcd_request ends\n");
-#endif
-}
-
-
-/* VFS calls */
-
-static int opt_ioctl(struct inode *ip, struct file *fp,
-			unsigned int cmd, unsigned long arg) {
-	static struct opt_Play_msf opt_Play;	/* pause position */
-	int err;
-#ifdef DEBUG_VFS
-	printk("optcd: starting opt_ioctl, command 0x%x\n", cmd);
-#endif
-	if (!ip)
-		return -EINVAL;
-	if (optGetStatus() < 0)
-		return -EIO;
-	if ((err = optUpdateToc()) < 0)
-		return err;
-
-	switch (cmd) {
-	case CDROMPAUSE: {
-		struct opt_Toc qInfo;
-
-		if (optAudioStatus != CDROM_AUDIO_PLAY)
-			return -EINVAL;
-		if (optGetQChannelInfo(&qInfo) < 0) {
-			/* didn't get q channel info */
-			optAudioStatus = CDROM_AUDIO_NO_STATUS;
-			return 0;
-		}
-		opt_Play.start = qInfo.diskTime;	/* restart point */
-		if (optCmd(COMPAUSEON) < 0)
-			return -EIO;
-		optAudioStatus = CDROM_AUDIO_PAUSED;
-		break;
-	}
-	case CDROMRESUME:
-		if (optAudioStatus != CDROM_AUDIO_PAUSED)
-			return -EINVAL;
-		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
-			optAudioStatus = CDROM_AUDIO_ERROR;
-			return -EIO;
-		}
-		optAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-	case CDROMPLAYMSF: {
-		int st;
-		struct cdrom_msf msf;
-
-		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
-			return st;
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
-		opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
-		opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
-		opt_Play.end.min = bin2bcd(msf.cdmsf_min1);
-		opt_Play.end.sec = bin2bcd(msf.cdmsf_sec1);
-		opt_Play.end.frame = bin2bcd(msf.cdmsf_frame1);
-		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
-			optAudioStatus = CDROM_AUDIO_ERROR;
-			return -EIO;
-		}
-		optAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-	}
-	case CDROMPLAYTRKIND: {
-		int st;
-		struct cdrom_ti ti;
-
-		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof ti)))
-			return st;
-		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
-		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;
-		opt_Play.start = Toc[ti.cdti_trk0].diskTime;
-		opt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
-#ifdef DEBUG_VFS
-		printk("optcd: play %02x:%02x.%02x to %02x:%02x.%02x\n",
-			opt_Play.start.min,
-			opt_Play.start.sec,
-			opt_Play.start.frame,
-			opt_Play.end.min,
-			opt_Play.end.sec,
-			opt_Play.end.frame);
-#endif
-		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
-			optAudioStatus = CDROM_AUDIO_ERROR;
-			return -EIO;
-		}
-		optAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-	}
-	case CDROMREADTOCHDR: {		/* Read the table of contents header. */
-		int st;
-		struct cdrom_tochdr tocHdr;
-
-		if ((st = verify_area(VERIFY_WRITE,(void *)arg,sizeof tocHdr)))
-			return st;
-		if (!optTocUpToDate)
-			optGetDiskInfo();
-		tocHdr.cdth_trk0 = DiskInfo.first;
-		tocHdr.cdth_trk1 = DiskInfo.last;
-		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
-		break;
-	}
-	case CDROMREADTOCENTRY: {	/* Read a table of contents entry. */
-		int st;
-		struct cdrom_tocentry entry;
-		struct opt_Toc *tocPtr;
-
-		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof entry)))
-			return st;
-		if ((st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry)))
-			return st;
-		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
-		if (!optTocUpToDate)
-			optGetDiskInfo();
-		if (entry.cdte_track == CDROM_LEADOUT)
-			tocPtr = &Toc[DiskInfo.last + 1];
-		else if (entry.cdte_track > DiskInfo.last
-			|| entry.cdte_track < DiskInfo.first)
-			return -EINVAL;
-		else
-			tocPtr = &Toc[entry.cdte_track];
-		entry.cdte_adr = tocPtr -> ctrl_addr;
-		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
-		switch (entry.cdte_format) {
-		case CDROM_LBA:
-			entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
-			break;
-		case 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);
-			break;
-		default:
-			return -EINVAL;
-		}
-		memcpy_tofs((void *) arg, &entry, sizeof entry);
-		break;
-	}
-	case CDROMSTOP:
-		optCmd(COMSTOP);
-		optAudioStatus = CDROM_AUDIO_NO_STATUS;
-		break;
-	case CDROMSTART:
-		optCmd(COMCLOSE);	/* What else can we do? */
-		break;
-	case CDROMEJECT:
-		optCmd(COMUNLOCK);
-		optCmd(COMOPEN);
-		break;
-	case CDROMVOLCTRL: {
-		int st;
-		struct cdrom_volctrl volctrl;
-
-		if ((st = verify_area(VERIFY_READ, (void *) arg,
-				sizeof(volctrl))))
-			return st;
-		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
-		opt_Play.start.min = 0x10;
-		opt_Play.start.sec = 0x32;
-		opt_Play.start.frame = volctrl.channel0;
-		opt_Play.end.min = volctrl.channel1;
-		opt_Play.end.sec = volctrl.channel2;
-		opt_Play.end.frame = volctrl.channel3;
-		if (optPlayCmd(COMCHCTRL, &opt_Play) < 0)
-			return -EIO;
-		break;
-	}
-	case CDROMSUBCHNL: {	/* Get subchannel info */
-		int st;
-		struct cdrom_subchnl subchnl;
-		struct opt_Toc qInfo;
-
-		if ((st = verify_area(VERIFY_READ,
-				(void *) arg, sizeof subchnl)))
-			return st;
-		if ((st = verify_area(VERIFY_WRITE,
-				(void *) arg, sizeof subchnl)))
-			return st;
-		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
-		if (optGetQChannelInfo(&qInfo) < 0)
-			return -EIO;
-		subchnl.cdsc_audiostatus = optAudioStatus;
-		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);
-		switch (subchnl.cdsc_format) {
-		case CDROM_LBA:
-			subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
-			subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
-			break;
-		case 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);
-			break;
-		default:
-			return -EINVAL;
-		}
-		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
-		break;
-	}
-	case CDROMREADMODE1: {
-		int st;
-		struct cdrom_msf msf;
-		char buf[OPT_BLOCKSIZE];
-
-		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
-			return st;
-		if ((st = verify_area(VERIFY_WRITE,(void *)arg,OPT_BLOCKSIZE)))
-			return st;
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
-		opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
-		opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
-		opt_Play.end.min = 0;
-		opt_Play.end.sec = 0;
-		opt_Play.end.frame = 1;	/* read only one frame */
-		st = optReadCmd(COMREAD, &opt_Play);
-#ifdef DEBUG_VFS
-		printk("optcd: COMREAD status 0x%x\n", st);
-#endif
-		sleep_dten_low();	/* error checking here?? */
-		optReadData(buf, OPT_BLOCKSIZE);
-		memcpy_tofs((void *) arg, &buf, OPT_BLOCKSIZE);
-		break;
-	}
-	case CDROMMULTISESSION:
-		return -EINVAL; /* unluckily, not implemented yet */
-
-	default:
-		return -EINVAL;
-	}
-#ifdef DEBUG_VFS
-	printk("optcd: exiting opt_ioctl\n");
-#endif
-	return 0;
-}
-
-static int optPresent = 0;
-static int opt_open_count = 0;
-
-/* Open device special file; check that a disk is in. */
-static int opt_open(struct inode *ip, struct file *fp) {
-#ifdef DEBUG_VFS
-	printk("optcd: starting opt_open\n");
-#endif
-	if (!optPresent)
-		return -ENXIO;		/* no hardware */
-	if (!opt_open_count && opt_state == OPT_S_IDLE) {
-		int st;
-		opt_invalidate_buffers();
-		if ((st = optGetStatus()) < 0)
-			return -EIO;
-		if (st & ST_DOOR_OPEN) {
-			optCmd(COMCLOSE);			/* close door */
-			if ((st = optGetStatus()) < 0)		/* try again */
-				return -EIO;
-		}
-		if (st & (ST_DOOR_OPEN|ST_DRVERR)) {
-			printk("optcd: no disk or door open\n");
-			return -EIO;
-		}
-		if (optUpdateToc() < 0)
-			return -EIO;
-	}
-	opt_open_count++;
-	MOD_INC_USE_COUNT;
-	optCmd(COMLOCK);		/* Lock door */
-#ifdef DEBUG_VFS
-	printk("optcd: exiting opt_open\n");
-#endif
-	return 0;
-}
-
-/* Release device special file; flush all blocks from the buffer cache */
-static void opt_release(struct inode *ip, struct file *fp) {
-#ifdef DEBUG_VFS
-	printk("optcd: executing opt_release\n");
-	printk("inode: %p, inode -> i_rdev: 0x%x, file: %p\n",
-		ip, ip -> i_rdev, fp);
-#endif
-	if (!--opt_open_count) {
-		opt_invalidate_buffers();
-		sync_dev(ip -> i_rdev);
-		invalidate_buffers(ip -> i_rdev);
-		CLEAR_TIMER;
-		optCmd(COMUNLOCK);	/* Unlock door */
-	}
-	MOD_DEC_USE_COUNT;
-}
-
-
-/* Initialisation */
-
-static int version_ok(void) {
-	char devname[100];
-	int count, i, ch;
-
-	if (optCmd(COMVERSION) < 0)
-		return 0;
-	if ((count = optGetData()) < 0)
-		return 0;
-	for (i = 0, ch = -1; count > 0; count--) {
-		if ((ch = optGetData()) < 0)
-			break;
-		if (i < 99)
-			devname[i++] = ch;
-	}
-	devname[i] = '\0';
-	if (ch < 0)
-		return 0;
-	printk("optcd: Device %s detected\n", devname);
-	return ((devname[0] == 'D')
-	     && (devname[1] == 'O')
-	     && (devname[2] == 'L')
-	     && (devname[3] == 'P')
-	     && (devname[4] == 'H')
-	     && (devname[5] == 'I')
-	     && (devname[6] == 'N'));
-}
-
-
-static struct file_operations opt_fops = {
-	NULL,		/* lseek - default */
-	block_read,	/* read - general block-dev read */
-	block_write,	/* write - general block-dev write */
-	NULL,		/* readdir - bad */
-	NULL,		/* select */
-	opt_ioctl,	/* ioctl */
-	NULL,		/* mmap */
-	opt_open,	/* open */
-	opt_release,	/* release */
-	NULL,		/* fsync */
-	NULL,		/* fasync */
-	NULL,		/* media change */
-	NULL		/* revalidate */
-};
-
-
-/* Get kernel parameter when used as a kernel driver */
-void optcd_setup(char *str, int *ints) {
-	if (ints[0] > 0)
-		optcd_port = ints[1];
-}
-
-/*
- * Test for presence of drive and initialize it. Called at boot time.
- */
-
-int optcd_init(void) {
-	if (optcd_port <= 0) {
-		printk("optcd: no Optics Storage CDROM Initialization\n");
-		return -EIO;
-	}
-	if (check_region(optcd_port, 4)) {
-		printk("optcd: conflict, I/O port 0x%x already used\n",
-			optcd_port);
-		return -EIO;
-	}
-
-	if (!check_region(ISP16_DRIVE_SET_PORT, 5)) {
-	/* If someone else has'nt already reserved these ports,
-	   probe for an ISP16 interface card, and enable SONY mode
-	   with no interrupts and no DMA. (As far as I know, all optics
-	   drives come with a SONY interface.) */
-  if ( (isp16_type=isp16_detect()) < 0 )
-    printk( "No ISP16 cdrom interface found.\n" );
-  else {
-    u_char expected_drive;
-
-    printk( "ISP16 cdrom interface (%s optional IDE) detected.\n",
-      (isp16_type==2)?"with":"without" );
-
-    expected_drive = (isp16_type?ISP16_SANYO1:ISP16_SANYO0);
-
-    if ( isp16_config( optcd_port, ISP16_SONY, 0, 0 ) < 0 ) {
-      printk( "ISP16 cdrom interface has not been properly configured.\n" );
-      return -EIO;
-    }
-  }
-	}
-
-	if (!optResetDrive()) {
-		printk("optcd: drive at 0x%x not ready\n", optcd_port);
-		return -EIO;
-	}
-	if (!version_ok()) {
-		printk("optcd: unknown drive detected; aborting\n");
-		return -EIO;
-	}
-	if (optCmd(COMINITDOUBLE) < 0) {
-		printk("optcd: cannot init double speed mode\n");
-		return -EIO;
-	}
-	if (register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
-	{
-		printk("optcd: unable to get major %d\n", MAJOR_NR);
-		return -EIO;
-	}
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = 4;
-	request_region(optcd_port, 4, "optcd");
-	optPresent = 1;
-	printk("optcd: 8000 AT CDROM at 0x%x\n", optcd_port);
-	return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void) {
-	if (MOD_IN_USE) {
-		printk("optcd: module in use - can't remove it.\n");
-	return;
-	}
-	if ((unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL)) {
-		printk("optcd: what's that: can't unregister\n");
-		return;
-	}
-	release_region(optcd_port, 4);
-	printk("optcd: module released.\n");
-}
-#endif MODULE
-
-
-/*
- * -- ISP16 detection and configuration
- *
- *    Copyright (c) 1995, Eric van der Maarel <maarel@marin.nl>
- *
- *    Version 0.5
- *
- *    Detect cdrom interface on ISP16 soundcard.
- *    Configure cdrom interface.
- *
- *    Algorithm for the card with no IDE support option taken
- *    from the CDSETUP.SYS driver for MSDOS,
- *    by OPTi Computers, version 2.03.
- *    Algorithm for the IDE supporting ISP16 as communicated
- *    to me by Vadim Model and Leo Spiekman.
- *
- *    Use, modifification or redistribution of this software is
- *    allowed under the terms of the GPL.
- *
- */
-
-
-#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p))
-#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p))
-
-static short
-isp16_detect(void)
-{
-
-  if ( !( isp16_with_ide__detect() < 0 ) )
-    return(2);
-  else
-    return( isp16_no_ide__detect() );
-}
-
-static short
-isp16_no_ide__detect(void)
-{
-  u_char ctrl;
-  u_char enable_cdrom;
-  u_char io;
-  short i = -1;
-
-  isp16_ctrl = ISP16_NO_IDE__CTRL;
-  isp16_enable_cdrom_port = ISP16_NO_IDE__ENABLE_CDROM_PORT;
-
-  /* read' and write' are a special read and write, respectively */
-
-  /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */
-  ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC;
-  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-
-  /* read' 3,4 and 5-bit from the cdrom enable port */
-  enable_cdrom = ISP16_IN( ISP16_NO_IDE__ENABLE_CDROM_PORT ) & 0x38;
-
-  if ( !(enable_cdrom & 0x20) ) {  /* 5-bit not set */
-    /* read' last 2 bits of ISP16_IO_SET_PORT */
-    io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03;
-    if ( ((io&0x01)<<1) == (io&0x02) ) {  /* bits are the same */
-      if ( io == 0 ) {  /* ...the same and 0 */
-        i = 0;
-        enable_cdrom |= 0x20;
-      }
-      else {  /* ...the same and 1 */  /* my card, first time 'round */
-        i = 1;
-        enable_cdrom |= 0x28;
-      }
-      ISP16_OUT( ISP16_NO_IDE__ENABLE_CDROM_PORT, enable_cdrom );
-    }
-    else {  /* bits are not the same */
-      ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-      return(i); /* -> not detected: possibly incorrect conclusion */
-    }
-  }
-  else if ( enable_cdrom == 0x20 )
-    i = 0;
-  else if ( enable_cdrom == 0x28 )  /* my card, already initialised */
-    i = 1;
-
-  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-
-  return(i);
-}
-
-static short
-isp16_with_ide__detect(void)
-{
-  u_char ctrl;
-  u_char tmp;
-
-  isp16_ctrl = ISP16_IDE__CTRL;
-  isp16_enable_cdrom_port = ISP16_IDE__ENABLE_CDROM_PORT;
-
-  /* read' and write' are a special read and write, respectively */
-
-  /* read' ISP16_CTRL_PORT and save */
-  ctrl = ISP16_IN( ISP16_CTRL_PORT );
-
-  /* write' zero to the ctrl port and get response */
-  ISP16_OUT( ISP16_CTRL_PORT, 0 );
-  tmp = ISP16_IN( ISP16_CTRL_PORT );
-
-  if ( tmp != 2 )  /* isp16 with ide option not detected */
-    return(-1);
-
-  /* restore ctrl port value */
-  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-  
-  return(2);
-}
-
-static short
-isp16_config( int base, u_char drive_type, int irq, int dma )
-{
-  u_char base_code;
-  u_char irq_code;
-  u_char dma_code;
-  u_char i;
-
-  if ( (drive_type == ISP16_MITSUMI) && (dma != 0) )
-    printk( "Mitsumi cdrom drive has no dma support.\n" );
-
-  switch (base) {
-  case 0x340: base_code = ISP16_BASE_340; break;
-  case 0x330: base_code = ISP16_BASE_330; break;
-  case 0x360: base_code = ISP16_BASE_360; break;
-  case 0x320: base_code = ISP16_BASE_320; break;
-  default:
-    printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base );
-    return(-1);
-  }
-  switch (irq) {
-  case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */
-  case 5: irq_code = ISP16_IRQ_5;
-          printk( "Irq 5 shouldn't be used by cdrom interface on ISP16,"
-            " due to possible conflicts with the soundcard.\n");
-          break;
-  case 7: irq_code = ISP16_IRQ_7;
-          printk( "Irq 7 shouldn't be used by cdrom interface on ISP16,"
-            " due to possible conflicts with the soundcard.\n");
-          break;
-  case 3: irq_code = ISP16_IRQ_3; break;
-  case 9: irq_code = ISP16_IRQ_9; break;
-  case 10: irq_code = ISP16_IRQ_10; break;
-  case 11: irq_code = ISP16_IRQ_11; break;
-  default:
-    printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq );
-    return(-1);
-  }
-  switch (dma) {
-  case 0: dma_code = ISP16_DMA_X; break;  /* disable dma */
-  case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16,"
-            " due to conflict with the soundcard.\n");
-          return(-1); break;
-  case 3: dma_code = ISP16_DMA_3; break;
-  case 5: dma_code = ISP16_DMA_5; break;
-  case 6: dma_code = ISP16_DMA_6; break;
-  case 7: dma_code = ISP16_DMA_7; break;
-  default:
-    printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma );
-    return(-1);
-  }
-
-  if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 &&
-    drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 &&
-    drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI &&
-    drive_type != ISP16_DRIVE_X ) {
-    printk( "Drive type (code 0x%02X) not supported by cdrom"
-     " interface on ISP16.\n", drive_type );
-    return(-1);
-  }
-
-  /* set type of interface */
-  i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK;  /* clear some bits */
-  ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type );
-
-  /* enable cdrom on interface with ide support */
-  if ( isp16_type > 1 )
-    ISP16_OUT( isp16_enable_cdrom_port, ISP16_ENABLE_CDROM );
-
-  /* set base address, irq and dma */
-  i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK;  /* keep some bits */
-  ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code );
-
-  return(0);
-}
diff -u --recursive --new-file v1.3.35/linux/drivers/block/ramdisk.c linux/drivers/block/ramdisk.c
--- v1.3.35/linux/drivers/block/ramdisk.c	Mon Sep 18 14:53:50 1995
+++ linux/drivers/block/ramdisk.c	Sat Oct 21 19:37:35 1995
@@ -20,7 +20,7 @@
 #include <asm/segment.h>
 
 #define MAJOR_NR  MEM_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
 
 #define RAMDISK_MINOR	1
 
diff -u --recursive --new-file v1.3.35/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c
--- v1.3.35/linux/drivers/block/sbpcd.c	Tue Oct 10 18:46:32 1995
+++ linux/drivers/block/sbpcd.c	Thu Jan  1 02:00:00 1970
@@ -1,5422 +0,0 @@
-/*
- *  sbpcd.c   CD-ROM device driver for the whole family of IDE-style
- *            Kotobuki/Matsushita/Panasonic CR-5xx drives for
- *            SoundBlaster ("Pro" or "16 ASP" or compatible) cards
- *            and for "no-sound" interfaces like Lasermate and the
- *            Panasonic CI-101P.
- *            Also for the Longshine LCS-7260 drive.
- *            Also for the IBM "External ISA CD-Rom" drive.
- *            Also for the CreativeLabs CD200 drive (but I still need some
- *            detailed bug reports).
- *            Also for the TEAC CD-55A drive.
- *            Not for Sanyo drives (but sjcd is there...).
- *            Not for any other Funai drives than E2550UA (="CD200" with "F").
- *
- *  NOTE:     This is release 3.9.
- *
- *  VERSION HISTORY
- *
- *  0.1  initial release, April/May 93, after mcd.c (Martin Harriss)
- *
- *  0.2  the "repeat:"-loop in do_sbpcd_request did not check for
- *       end-of-request_queue (resulting in kernel panic).
- *       Flow control seems stable, but throughput is not better.  
- *
- *  0.3  interrupt locking totally eliminated (maybe "inb" and "outb"
- *       are still locking) - 0.2 made keyboard-type-ahead losses.
- *       check_sbpcd_media_change added (to use by isofs/inode.c)
- *       - but it detects almost nothing.
- *
- *  0.4  use MAJOR 25 definitely.
- *       Almost total re-design to support double-speed drives and
- *       "naked" (no sound) interface cards ("LaserMate" interface type).
- *       Flow control should be exact now.
- *       Don't occupy the SbPro IRQ line (not needed either); will
- *       live together with Hannu Savolainen's sndkit now.
- *	 Speeded up data transfer to 150 kB/sec, with help from Kai
- *       Makisara, the "provider" of the "mt" tape utility.
- *       Give "SpinUp" command if necessary.
- *       First steps to support up to 4 drives (but currently only one).
- *       Implemented audio capabilities - workman should work, xcdplayer
- *       gives some problems.
- *       This version is still consuming too much CPU time, and
- *       sleeping still has to be worked on.
- *       During "long" implied seeks, it seems possible that a 
- *       ReadStatus command gets ignored. That gives the message
- *       "ResponseStatus timed out" (happens about 6 times here during
- *       a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is
- *       handled without data error, but it should get done better.
- *
- *  0.5  Free CPU during waits (again with help from Kai Makisara).
- *       Made it work together with the LILO/kernel setup standard.
- *       Included auto-probing code, as suggested by YGGDRASIL.
- *       Formal redesign to add DDI debugging.
- *       There are still flaws in IOCTL (workman with double speed drive).
- *
- *  1.0  Added support for all drive IDs (0...3, no longer only 0)
- *       and up to 4 drives on one controller.
- *       Added "#define MANY_SESSION" for "old" multi session CDs.
- *
- *  1.1  Do SpinUp for new drives, too.
- *       Revised for clean compile under "old" kernels (0.99pl9).
- *
- *  1.2  Found the "workman with double-speed drive" bug: use the driver's
- *       audio_state, not what the drive is reporting with ReadSubQ.
- *
- *  1.3  Minor cleanups.
- *       Refinements regarding Workman.
- *
- *  1.4  Read XA disks (PhotoCDs) with "old" drives, too (but only the first
- *       session - no chance to fully access a "multi-session" CD).
- *       This currently still is too slow (50 kB/sec) - but possibly
- *       the old drives won't do it faster.
- *       Implemented "door (un)lock" for new drives (still does not work
- *       as wanted - no lock possible after an unlock).
- *       Added some debugging printout for the UPC/EAN code - but my drives 
- *       return only zeroes. Is there no UPC/EAN code written?
- *
- *  1.5  Laborate with UPC/EAN code (not better yet).
- *       Adapt to kernel 1.1.8 change (have to explicitly include
- *       <linux/string.h> now).
- *
- *  1.6  Trying to read audio frames as data. Impossible with the current
- *       drive firmware levels, as it seems. Awaiting any hint. ;-)
- *       Changed "door unlock": repeat it until success.
- *       Changed CDROMSTOP routine (stop somewhat "softer" so that Workman
- *       won't get confused).
- *       Added a third interface type: Sequoia S-1000, as used with the SPEA
- *       Media FX sound card. This interface (usable for Sony and Mitsumi 
- *       drives, too) needs a special configuration setup and behaves like a 
- *       LaserMate type after that. Still experimental - I do not have such
- *       an interface.
- *       Use the "variable BLOCK_SIZE" feature (2048). But it does only work
- *       if you give the mount option "block=2048".
- *       The media_check routine is currently disabled; now that it gets
- *       called as it should I fear it must get synchronized for not to
- *       disturb the normal driver's activity.
- *
- *  2.0  Version number bumped - two reasons:
- *       - reading audio tracks as data works now with CR-562 and CR-563. We
- *       currently do it by an IOCTL (yet has to get standardized), one frame
- *       at a time; that is pretty slow. But it works.
- *       - we are maintaining now up to 4 interfaces (each up to 4 drives):
- *       did it the easy way - a different MAJOR (25, 26, ...) and a different
- *       copy of the driver (sbpcd.c, sbpcd2.c, sbpcd3.c, sbpcd4.c - only
- *       distinguished by the value of SBPCD_ISSUE and the driver's name),
- *       and a common sbpcd.h file.
- *       Bettered the "ReadCapacity error" problem with old CR-52x drives (the
- *       drives sometimes need a manual "eject/insert" before work): just
- *       reset the drive and do again. Needs lots of resets here and sometimes
- *       that does not cure, so this can't be the solution.
- *
- *  2.1  Found bug with multisession CDs (accessing frame 16).
- *       "read audio" works now with address type CDROM_MSF, too.
- *       Bigger audio frame buffer: allows reading max. 4 frames at time; this
- *       gives a significant speedup, but reading more than one frame at once
- *       gives missing chunks at each single frame boundary.
- *
- *  2.2  Kernel interface cleanups: timers, init, setup, media check.
- *
- *  2.3  Let "door lock" and "eject" live together.
- *       Implemented "close tray" (done automatically during open).
- *
- *  2.4  Use different names for device registering.
- *
- *  2.5  Added "#if EJECT" code (default: enabled) to automatically eject
- *       the tray during last call to "sbpcd_release".
- *       Added "#if JUKEBOX" code (default: disabled) to automatically eject
- *       the tray during call to "sbpcd_open" if no disk is in.
- *       Turn on the CD volume of "compatible" sound cards, too; just define
- *       SOUND_BASE (in sbpcd.h) accordingly (default: disabled).
- *
- *  2.6  Nothing new.  
- *
- *  2.7  Added CDROMEJECT_SW ioctl to set the "EJECT" behavior on the fly:
- *       0 disables, 1 enables auto-ejecting. Useful to keep the tray in
- *       during shutdown.
- *
- *  2.8  Added first support (still BETA, I need feedback or a drive) for
- *       the Longshine LCS-7260 drives. They appear as double-speed drives
- *       using the "old" command scheme, extended by tray control and door
- *       lock functions.
- *       Found (and fixed preliminary) a flaw with some multisession CDs: we
- *       have to re-direct not only the accesses to frame 16 (the isofs
- *       routines drive it up to max. 100), but also those to the continuation
- *       (repetition) frames (as far as they exist - currently set fix as
- *       16..20).
- *       Changed default of the "JUKEBOX" define. If you use this default,
- *       your tray will eject if you try to mount without a disk in. Next
- *       mount command will insert the tray - so, just fill in a disk. ;-)
- *
- *  2.9  Fulfilled the Longshine LCS-7260 support; with great help and
- *       experiments by Serge Robyns.
- *       First attempts to support the TEAC CD-55A drives; but still not
- *       usable yet.
- *       Implemented the CDROMMULTISESSION ioctl; this is an attempt to handle
- *       multi session CDs more "transparent" (redirection handling has to be
- *       done within the isofs routines, and only for the special purpose of
- *       obtaining the "right" volume descriptor; accesses to the raw device
- *       should not get redirected).
- *
- *  3.0  Just a "normal" increment, with some provisions to do it better. ;-)
- *       Introduced "#define READ_AUDIO" to specify the maximum number of 
- *       audio frames to grab with one request. This defines a buffer size
- *       within kernel space; a value of 0 will reserve no such space and
- *       disable the CDROMREADAUDIO ioctl. A value of 75 enables the reading
- *       of a whole second with one command, but will use a buffer of more
- *       than 172 kB.
- *       Started CD200 support. Drive detection should work, but nothing
- *       more.
- *
- *  3.1  Working to support the CD200 and the Teac CD-55A drives.
- *       AT-BUS style device numbering no longer used: use SCSI style now.
- *       So, the first "found" device has MINOR 0, regardless of the
- *       jumpered drive ID. This implies modifications to the /dev/sbpcd*
- *       entries for some people, but will help the DAU (german TLA, english:
- *       "newbie", maybe ;-) to install his "first" system from a CD.
- *
- *  3.2  Still testing with CD200 and CD-55A drives.
- *
- *  3.3  Working with CD200 support.
- *
- *  3.4  Auto-probing stops if an address of 0 is seen (to be entered with
- *       the kernel command line).
- *       Made the driver "loadable". If used as a module, "audio copy" is
- *       disabled, and the internal read ahead data buffer has a reduced size
- *       of 4 kB; so, throughput may be reduced a little bit with slow CPUs.
- *
- *  3.5  Provisions to handle weird photoCDs which have an interrupted
- *       "formatting" immediately after the last frames of some files: simply
- *       never "read ahead" with MultiSession CDs. By this, CPU usage may be
- *       increased with those CDs, and there may be a loss in speed.
- *       Re-structured the messaging system.
- *       The "loadable" version no longer has a limited READ_AUDIO buffer
- *       size.
- *       Removed "MANY_SESSION" handling for "old" multi session CDs.
- *       Added "private" IOCTLs CDROMRESET and CDROMVOLREAD.
- *       Started again to support the TEAC CD-55A drives, now that I found
- *       the money for "my own" drive. ;-)
- *       The TEAC CD-55A support is fairly working now.
- *       I have measured that the drive "delivers" at 600 kB/sec (even with
- *       bigger requests than the drive's 64 kB buffer can satisfy), but
- *       the "real" rate does not exceed 520 kB/sec at the moment. 
- *       Caused by the various changes to build in TEAC support, the timed
- *       loops are de-optimized at the moment (less throughput with CR-52x
- *       drives, and the TEAC will give speed only with SBP_BUFFER_FRAMES 64).
- *
- *  3.6  Fixed TEAC data read problems with SbPro interfaces.
- *       Initial size of the READ_AUDIO buffer is 0. Can get set to any size
- *       during runtime.
- *
- *  3.7  Introduced MAX_DRIVES for some poor interface cards (seen with TEAC
- *       drives) which allow only one drive (ID 0); this avoids repetitive
- *       detection under IDs 1..3. 
- *       Elongated cmd_out_T response waiting; necessary for photo CDs with
- *       a lot of sessions.
- *       Bettered the sbpcd_open() behavior with TEAC drives.
- *
- *  3.8  Elongated max_latency for CR-56x drives.
- *
- *  3.9  Finally fixed the long-known SoundScape/SPEA/Sequoia S-1000 interface
- *       configuration bug.
- *       Now Corey, Heiko, Ken, Leo, Vadim/Eric & Werner are invited to copy
- *       the config_spea() routine into their drivers. ;-)
- *
- *
- *  TODO
- *
- *     disk change detection
- *     synchronize multi-activity
- *        (data + audio + ioctl + disk change, multiple drives)
- *     implement "read all subchannel data" (96 bytes per frame)
- *     check if CDROMPLAYMSF can cause a hang
- *
- *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
- *     elaborated speed-up experiments (and the fabulous results!), for
- *     the "push" towards load-free wait loops, and for the extensive mail
- *     thread which brought additional hints and bug fixes.
- *
- *   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
- *
- *                  If you change this software, you should mail a .diff
- *                  file with some description lines to emoenke@gwdg.de.
- *                  I want to know about it.
- *
- *                  If you are the editor of a Linux CD, you should
- *                  enable sbpcd.c within your boot floppy kernel and
- *                  send me one of your CDs for free.
- *
- *   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.
- *
- *   You should have received a copy of the GNU General Public License
- *   (for example /usr/src/linux/COPYING); if not, write to the Free
- *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef SBPCD_ISSUE
-#define SBPCD_ISSUE 1
-#endif SBPCD_ISSUE
-
-#include <linux/config.h>
-
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[]=UTS_RELEASE;
-#endif
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif MODULE
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/major.h> 
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-#include <stdarg.h>
-#include <linux/sbpcd.h>
-
-#if !(SBPCD_ISSUE-1)
-#define MAJOR_NR MATSUSHITA_CDROM_MAJOR
-#endif
-#if !(SBPCD_ISSUE-2)
-#define MAJOR_NR MATSUSHITA_CDROM2_MAJOR /* second driver issue */
-#endif
-#if !(SBPCD_ISSUE-3)
-#define MAJOR_NR MATSUSHITA_CDROM3_MAJOR /* third driver issue */
-#endif
-#if !(SBPCD_ISSUE-4)
-#define MAJOR_NR MATSUSHITA_CDROM4_MAJOR /* fourth driver issue */
-#endif
-
-#include "blk.h"
-
-#define VERSION "v3.9 Eberhard Moenkeberg <emoenke@gwdg.de>"
-
-/*==========================================================================*/
-/*
- * provisions for more than 1 driver issues
- * currently up to 4 drivers, expandable
- */
-#if !(SBPCD_ISSUE-1)
-#define DO_SBPCD_REQUEST(a) do_sbpcd_request(a)
-#define SBPCD_INIT(a) sbpcd_init(a)
-#endif
-#if !(SBPCD_ISSUE-2)
-#define DO_SBPCD_REQUEST(a) do_sbpcd2_request(a)
-#define SBPCD_INIT(a) sbpcd2_init(a)
-#endif
-#if !(SBPCD_ISSUE-3)
-#define DO_SBPCD_REQUEST(a) do_sbpcd3_request(a)
-#define SBPCD_INIT(a) sbpcd3_init(a)
-#endif
-#if !(SBPCD_ISSUE-4)
-#define DO_SBPCD_REQUEST(a) do_sbpcd4_request(a)
-#define SBPCD_INIT(a) sbpcd4_init(a)
-#endif
-/*==========================================================================*/
-#if SBPCD_DIS_IRQ
-#define SBPCD_CLI cli()
-#define SBPCD_STI sti()
-#else
-#define SBPCD_CLI
-#define SBPCD_STI
-#endif SBPCD_DIS_IRQ
-/*==========================================================================*/
-/*
- * auto-probing address list
- * inspired by Adam J. Richter from Yggdrasil
- *
- * still not good enough - can cause a hang.
- *   example: a NE 2000 ethernet card at 300 will cause a hang probing 310.
- * if that happens, reboot and use the LILO (kernel) command line.
- * The possibly conflicting ethernet card addresses get NOT probed 
- * by default - to minimize the hang possibilities. 
- *
- * The SB Pro addresses get "mirrored" at 0x6xx and some more locations - to
- * avoid a type error, the 0x2xx-addresses must get checked before 0x6xx.
- *
- * send mail to emoenke@gwdg.de if your interface card is not FULLY
- * represented here.
- */
-#if !(SBPCD_ISSUE-1)
-static int sbpcd[] = 
-{
-	CDROM_PORT, SBPRO, /* probe with user's setup first */
-#if DISTRIBUTION
-	0x230, 1, /* Soundblaster Pro and 16 (default) */
-	0x300, 0, /* CI-101P (default), WDH-7001C (default),
-		     Galaxy (default), Reveal (one default) */
-	0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
-	0x260, 1, /* OmniCD */
-	0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default),
-		     Longshine LCS-6853 (default) */
-	0x338, 0, /* Reveal Sound Wave 32 card model #SC600 */
-	0x340, 0, /* Mozart sound card (default), Lasermate, CI-101P */
-	0x360, 0, /* Lasermate, CI-101P */
-	0x270, 1, /* Soundblaster 16 */
-	0x670, 0, /* "sound card #9" */
-	0x690, 0, /* "sound card #9" */
-	0x338, 2, /* SPEA Media FX, Ensonic SoundScape (default) */
-	0x328, 2, /* SPEA Media FX */
-	0x348, 2, /* SPEA Media FX */
-	0x634, 0, /* some newer sound cards */
-	0x638, 0, /* some newer sound cards */
-	0x230, 1, /* some newer sound cards */
-	/* due to incomplete address decoding of the SbPro card, these must be last */
-	0x630, 0, /* "sound card #9" (default) */
-	0x650, 0, /* "sound card #9" */
-#ifdef MODULE
-	/*
-	 * some "hazardous" locations (no harm with the loadable version)
-	 * (will stop the bus if a NE2000 ethernet card resides at offset -0x10)
-	 */
-	0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
-	0x350, 0, /* Lasermate, CI-101P */
-	0x358, 2, /* SPEA Media FX */
-	0x370, 0, /* Lasermate, CI-101P */
-	0x290, 1, /* Soundblaster 16 */
-	0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
-#endif MODULE
-#endif DISTRIBUTION
-};
-#else
-static int sbpcd[] = {CDROM_PORT, SBPRO}; /* probe with user's setup only */
-#endif
-
-#define NUM_PROBE  (sizeof(sbpcd) / sizeof(int))
-
-/*==========================================================================*/
-/*
- * the external references:
- */
-#if !(SBPCD_ISSUE-1)
-#ifdef CONFIG_SBPCD2
-extern int sbpcd2_init(void);
-#endif
-#ifdef CONFIG_SBPCD3
-extern int sbpcd3_init(void);
-#endif
-#ifdef CONFIG_SBPCD4
-extern int sbpcd4_init(void);
-#endif
-#endif
-
-/*==========================================================================*/
-
-#define INLINE inline
-
-/*==========================================================================*/
-/*
- * the forward references:
- */
-static void sbp_sleep(u_int);
-static void mark_timeout_delay(u_long);
-static void mark_timeout_data(u_long);
-#if 0
-static void mark_timeout_audio(u_long);
-#endif
-static void sbp_read_cmd(void);
-static int sbp_data(void);
-static int cmd_out(void);
-static int DiskInfo(void);
-static int sbpcd_chk_disk_change(kdev_t);
-
-/*==========================================================================*/
-
-/*
- * pattern for printk selection:
- *
- * (1<<DBG_INF)  necessary information
- * (1<<DBG_BSZ)  BLOCK_SIZE trace
- * (1<<DBG_REA)  "read" status trace
- * (1<<DBG_CHK)  "media check" trace
- * (1<<DBG_TIM)  datarate timer test
- * (1<<DBG_INI)  initialization trace
- * (1<<DBG_TOC)  tell TocEntry values
- * (1<<DBG_IOC)  ioctl trace
- * (1<<DBG_STA)  "ResponseStatus" trace
- * (1<<DBG_ERR)  "cc_ReadError" trace
- * (1<<DBG_CMD)  "cmd_out" trace
- * (1<<DBG_WRN)  give explanation before auto-probing
- * (1<<DBG_MUL)  multi session code test
- * (1<<DBG_IDX)  "drive_id != 0" test code
- * (1<<DBG_IOX)  some special information
- * (1<<DBG_DID)  drive ID test
- * (1<<DBG_RES)  drive reset info
- * (1<<DBG_SPI)  SpinUp test info
- * (1<<DBG_IOS)  ioctl trace: "subchannel"
- * (1<<DBG_IO2)  ioctl trace: general
- * (1<<DBG_UPC)  show UPC info
- * (1<<DBG_XA1)  XA mode debugging
- * (1<<DBG_LCK)  door (un)lock info
- * (1<<DBG_SQ1)   dump SubQ frame
- * (1<<DBG_AUD)  "read audio" debugging
- * (1<<DBG_SEQ)  Sequoia interface configuration trace
- * (1<<DBG_LCS)  Longshine LCS-7260 debugging trace
- * (1<<DBG_CD2)  MKE CD200 debugging trace
- * (1<<DBG_TEA)  TEAC CD-55A debugging trace
- * (1<<DBG_TE2)  TEAC CD-55A debugging trace, 2nd level
- * (1<<DBG_000)  unnecessary information
- */
-#if DISTRIBUTION
-static int sbpcd_debug = (1<<DBG_INF);
-#else
-static int sbpcd_debug = ((1<<DBG_INF) |
-			  (1<<DBG_TOC) |
-			  (1<<DBG_MUL) |
-			  (1<<DBG_UPC));
-#endif DISTRIBUTION
-
-static int sbpcd_ioaddr = CDROM_PORT;	/* default I/O base address */
-static int sbpro_type = SBPRO;
-static unsigned char setup_done = 0;
-static int CDo_command, CDo_reset;
-static int CDo_sel_i_d, CDo_enable;
-static int CDi_info, CDi_status, CDi_data;
-static int MIXER_addr, MIXER_data;
-static struct cdrom_msf msf;
-static struct cdrom_ti ti;
-static struct cdrom_tochdr tochdr;
-static struct cdrom_tocentry tocentry;
-static struct cdrom_subchnl SC;
-static struct cdrom_volctrl volctrl;
-static struct cdrom_read_audio read_audio;
-static struct cdrom_multisession ms_info;
-
-static unsigned char msgnum=0;
-static char msgbuf[80];
-
-static const char *str_sb = "SoundBlaster";
-static const char *str_sb_l = "soundblaster";
-static const char *str_lm = "LaserMate";
-static const char *str_sp = "SPEA";
-static const char *str_sp_l = "spea";
-static const char *str_ss = "SoundScape";
-static const char *str_ss_l = "soundscape";
-const char *type;
-
-#if !(SBPCD_ISSUE-1)
-static const char *major_name="sbpcd";
-#endif
-#if !(SBPCD_ISSUE-2)
-static const char *major_name="sbpcd2";
-#endif
-#if !(SBPCD_ISSUE-3)
-static const char *major_name="sbpcd3";
-#endif
-#if !(SBPCD_ISSUE-4)
-static const char *major_name="sbpcd4";
-#endif
-
-/*==========================================================================*/
-
-#if FUTURE
-static struct wait_queue *sbp_waitq = NULL;
-#endif FUTURE
-
-/*==========================================================================*/
-#define SBP_BUFFER_FRAMES 8 /* driver's own read_ahead, data mode */
-/*==========================================================================*/
-
-static u_char family0[]="MATSHITA"; /* MKE CR-52x */
-static u_char family1[]="CR-56";    /* MKE CR-56x */
-static u_char family2[]="CD200";    /* MKE CD200 */
-static u_char familyL[]="LCS-7260"; /* Longshine LCS-7260 */
-static u_char familyT[]="CD-55";    /* TEAC CD-55A */
-
-static u_int recursion=0; /* internal testing only */
-static u_int fatal_err=0; /* internal testing only */
-static u_int response_count=0;
-static u_int flags_cmd_out;
-static u_char cmd_type=0;
-static u_char drvcmd[10];
-static u_char infobuf[20];
-static u_char xa_head_buf[CD_XA_HEAD];
-static u_char xa_tail_buf[CD_XA_TAIL];
-
-static volatile u_char busy_data=0;
-static volatile u_char busy_audio=0; /* true semaphores would be safer */
-static u_long timeout;
-static volatile u_char timed_out_delay=0;
-static volatile u_char timed_out_data=0;
-#if 0
-static volatile u_char timed_out_audio=0;
-#endif
-static u_int datarate= 1000000;
-static u_int maxtim16=16000000;
-static u_int maxtim04= 4000000;
-static u_int maxtim02= 2000000;
-static u_int maxtim_8=   30000;
-#if LONG_TIMING
-static u_int maxtim_data= 9000;
-#else
-static u_int maxtim_data= 3000;
-#endif LONG_TIMING
-#if DISTRIBUTION
-static int n_retries=3;
-#else
-static int n_retries=1;
-#endif
-/*==========================================================================*/
-
-static int ndrives=0;
-static u_char drv_pattern[NR_SBPCD]={speed_auto,speed_auto,speed_auto,speed_auto};
-static int sbpcd_blocksizes[NR_SBPCD] = {0, };
-
-/*==========================================================================*/
-/*
- * drive space begins here (needed separate for each unit) 
- */
-static int d=0; /* DriveStruct index: drive number */
-
-static struct {
-	char drv_id;           /* "jumpered" drive ID or -1 */
-	char drv_sel;          /* drive select lines bits */
-	
-	char drive_model[9];
-	u_char firmware_version[4];
-	char f_eject;          /* auto-eject flag: 0 or 1 */
-	u_char *sbp_buf;       /* Pointer to internal data buffer,
-				  space allocated during sbpcd_init() */
-	u_int sbp_bufsiz;      /* size of sbp_buf (# of frames) */
-	int sbp_first_frame;   /* First frame in buffer */
-	int sbp_last_frame;    /* Last frame in buffer  */
-	int sbp_read_frames;   /* Number of frames being read to buffer */
-	int sbp_current;       /* Frame being currently read */
-	
-	u_char mode;           /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */
-	u_char *aud_buf;       /* Pointer to audio data buffer,
-				  space allocated during sbpcd_init() */
-	u_int sbp_audsiz;      /* size of aud_buf (# of raw frames) */
-	u_char drv_type;
-	u_char drv_options;
-	int status_bits;
-	u_char diskstate_flags;
-	u_char sense_byte;
-	
-	u_char CD_changed;
-	char open_count;
-	u_char error_byte;
-	
-	u_char f_multisession;
-	u_int lba_multi;
-	int first_session;
-	int last_session;
-	int track_of_last_session;
-	
-	u_char audio_state;
-	u_int pos_audio_start;
-	u_int pos_audio_end;
-	char vol_chan0;
-	u_char vol_ctrl0;
-	char vol_chan1;
-	u_char vol_ctrl1;
-#if 000 /* no supported drive has it */
-	char vol_chan2;
-	u_char vol_ctrl2;
-	char vol_chan3;
-	u_char vol_ctrl3;
-#endif 000
-	u_char volume_control; /* TEAC on/off bits */
-	
-	u_char SubQ_ctl_adr;
-	u_char SubQ_trk;
-	u_char SubQ_pnt_idx;
-	u_int SubQ_run_tot;
-	u_int SubQ_run_trk;
-	u_char SubQ_whatisthis;
-	
-	u_char UPC_ctl_adr;
-	u_char UPC_buf[7];
-	
-	int frame_size;
-	int CDsize_frm;
-	
-	u_char xa_byte; /* 0x20: XA capabilities */
-	u_char n_first_track; /* binary */
-	u_char n_last_track; /* binary (not bcd), 0x01...0x63 */
-	u_int size_msf; /* time of whole CD, position of LeadOut track */
-	u_int size_blk;
-	
-	u_char TocEnt_nixbyte; /* em */
-	u_char TocEnt_ctl_adr;
-	u_char TocEnt_number;
-	u_char TocEnt_format; /* em */
-	u_int TocEnt_address;
-	u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
-	
-	struct {
-		u_char nixbyte; /* em */
-		u_char ctl_adr; /* 0x4x: data, 0x0x: audio */
-		u_char number;
-		u_char format; /* em */ /* 0x00: lba, 0x01: msf */
-		u_int address;
-	} TocBuffer[MAX_TRACKS+1]; /* last entry faked */ 
-	
-	int in_SpinUp; /* CR-52x test flag */
-	int n_bytes; /* TEAC awaited response count */
-	u_char error_state, b3, b4; /* TEAC command error state */
-	u_char f_drv_error; /* TEAC command error flag */
-	u_char speed_byte;
-	int frmsiz;
-	u_char f_XA; /* 1: XA */
-	u_char type_byte; /* 0, 1, 3 */
-	u_char mode_xb_6;
-	u_char mode_yb_7;
-	u_char mode_xb_8;
-	u_char delay;
-	
-} D_S[NR_SBPCD];
-
-/*
- * drive space ends here (needed separate for each unit)
- */
-/*==========================================================================*/
-#if 0
-unsigned long cli_sti; /* for saving the processor flags */
-#endif
-/*==========================================================================*/
-static struct timer_list delay_timer = { NULL, NULL, 0, 0, mark_timeout_delay};
-static struct timer_list data_timer = { NULL, NULL, 0, 0, mark_timeout_data};
-#if 0
-static struct timer_list audio_timer = { NULL, NULL, 0, 0, mark_timeout_audio};
-#endif
-/*==========================================================================*/
-/*
- * DDI interface
- */
-static void msg(int level, const char *fmt, ...)
-{
-	char buf[256];
-	va_list args;
-	
-	if (!(sbpcd_debug&(1<<level))) return;
-	
-	msgnum++;
-	if (msgnum>99) msgnum=0;
-	sprintf(buf, "%s-%d [%02d]:  ", major_name, d, msgnum);
-	va_start(args, fmt);
-	vsprintf(&buf[15], fmt, args);
-	va_end(args);
-	printk(buf);
-	sbp_sleep(55); /* else messages get lost */
-	return;
-}
-/*==========================================================================*/
-/*
- * DDI interface: runtime trace bit pattern maintenance
- */
-static int sbpcd_dbg_ioctl(unsigned long arg, int level)
-{
-	switch(arg)
-	{
-	case 0:	/* OFF */
-		sbpcd_debug = DBG_INF;
-		break;
-		
-	default:
-		if (arg>=128) sbpcd_debug &= ~(1<<(arg-128));
-		else sbpcd_debug |= (1<<arg);
-	}
-	return (arg);
-}
-/*==========================================================================*/
-static void mark_timeout_delay(u_long i)
-{
-	timed_out_delay=1;
-#if 0
-	msg(DBG_TIM,"delay timer expired.\n");
-#endif
-}
-/*==========================================================================*/
-static void mark_timeout_data(u_long i)
-{
-	timed_out_data=1;
-#if 0
-	msg(DBG_TIM,"data timer expired.\n");
-#endif
-}
-/*==========================================================================*/
-#if 0
-static void mark_timeout_audio(u_long i)
-{
-	timed_out_audio=1;
-#if 0
-	msg(DBG_TIM,"audio timer expired.\n");
-#endif
-}
-#endif
-/*==========================================================================*/
-/*
- * Wait a little while (used for polling the drive).
- */
-static void sbp_sleep(u_int time)
-{
-#ifndef MODULE
-	if (current == task[0]) 
-	{
-		del_timer(&delay_timer);
-		delay_timer.expires=jiffies+time;
-		timed_out_delay=0;
-		add_timer(&delay_timer);
-		while (!timed_out_delay) ;
-		return;
-	}
-#endif MODULE
-	sti();
-	current->state = TASK_INTERRUPTIBLE;
-	current->timeout = jiffies + time;
-	schedule();
-	sti();
-}
-/*==========================================================================*/
-/*
- *  convert logical_block_address to m-s-f_number (3 bytes only)
- */
-static INLINE void lba2msf(int lba, u_char *msf)
-{
-	lba += CD_MSF_OFFSET;
-	msf[0] = lba / (CD_SECS*CD_FRAMES);
-	lba %= CD_SECS*CD_FRAMES;
-	msf[1] = lba / CD_FRAMES;
-	msf[2] = lba % CD_FRAMES;
-}
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- *  convert msf-bin to msf-bcd
- */
-static INLINE void bin2bcdx(u_char *p)  /* must work only up to 75 or 99 */
-{
-	*p=((*p/10)<<4)|(*p%10);
-}
-/*==========================================================================*/
-static INLINE u_int blk2msf(u_int blk)
-{
-	MSF msf;
-	u_int mm;
-	
-	msf.c[3] = 0;
-	msf.c[2] = (blk + CD_MSF_OFFSET) / (CD_SECS * CD_FRAMES);
-	mm = (blk + CD_MSF_OFFSET) % (CD_SECS * CD_FRAMES);
-	msf.c[1] = mm / CD_FRAMES;
-	msf.c[0] = mm % CD_FRAMES;
-	return (msf.n);
-}
-/*==========================================================================*/
-static INLINE u_int make16(u_char rh, u_char rl)
-{
-	return ((rh<<8)|rl);
-}
-/*==========================================================================*/
-static INLINE u_int make32(u_int rh, u_int rl)
-{
-	return ((rh<<16)|rl);
-}
-/*==========================================================================*/
-static INLINE u_char swap_nibbles(u_char i)
-{
-	return ((i<<4)|(i>>4));
-}
-/*==========================================================================*/
-static INLINE u_char byt2bcd(u_char i)
-{
-	return (((i/10)<<4)+i%10);
-}
-/*==========================================================================*/
-static INLINE u_char bcd2bin(u_char bcd)
-{
-	return ((bcd>>4)*10+(bcd&0x0F));
-}
-/*==========================================================================*/
-static INLINE int msf2blk(int msfx)
-{
-	MSF msf;
-	int i;
-	
-	msf.n=msfx;
-	i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_MSF_OFFSET;
-	if (i<0) return (0);
-	return (i);
-}
-/*==========================================================================*/
-/*
- *  convert m-s-f_number (3 bytes only) to logical_block_address 
- */
-static INLINE int msf2lba(u_char *msf)
-{
-	int i;
-	
-	i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_MSF_OFFSET;
-	if (i<0) return (0);
-	return (i);
-}
-/*==========================================================================*/
-/* evaluate cc_ReadError code */ 
-static int sta2err(int sta)
-{
-	if (famT_drive)
-	{
-		if (sta==0x00) return (0);
-		if (sta==0x01) return (-604); /* CRC error */
-		if (sta==0x02) return (-602); /* drive not ready */
-		if (sta==0x03) return (-607); /* unknown media */
-		if (sta==0x04) return (-612); /* general failure */
-		if (sta==0x05) return (0);
-		if (sta==0x06) return (-615); /* invalid disk change */
-		if (sta==0x0b) return (-612); /* general failure */
-		if (sta==0xff) return (-612); /* general failure */
-		return (0);
-	}
-	else
-	{
-		if (sta<=2) return (sta);
-		if (sta==0x05) return (-604); /* CRC error */
-		if (sta==0x06) return (-606); /* seek error */
-		if (sta==0x0d) return (-606); /* seek error */
-		if (sta==0x0e) return (-603); /* unknown command */
-		if (sta==0x14) return (-603); /* unknown command */
-		if (sta==0x0c) return (-611); /* read fault */
-		if (sta==0x0f) return (-611); /* read fault */
-		if (sta==0x10) return (-611); /* read fault */
-		if (sta>=0x16) return (-612); /* general failure */
-		D_S[d].CD_changed=0xFF;
-		if (sta==0x11) return (-615); /* invalid disk change (LCS: removed) */
-		if (famL_drive)
-			if (sta==0x12) return (-615); /* invalid disk change (inserted) */
-		return (-602); /* drive not ready */
-	}
-}
-/*==========================================================================*/
-static INLINE void clr_cmdbuf(void)
-{
-	int i;
-	
-	for (i=0;i<10;i++) drvcmd[i]=0;
-	cmd_type=0;
-}
-/*==========================================================================*/
-static void flush_status(void)
-{
-	int i;
-	
-#ifdef MODULE
-	sbp_sleep(15*HZ/10);
-	for (i=maxtim_data;i!=0;i--) inb(CDi_status);
-#else
-	if (current == task[0])
-		for (i=maxtim02;i!=0;i--) inb(CDi_status);
-	else 
-	{
-		sbp_sleep(15*HZ/10);
-		for (i=maxtim_data;i!=0;i--) inb(CDi_status);
-	}
-#endif MODULE
-}
-/*==========================================================================*/
-static int CDi_stat_loop(void)
-{
-	int i,j;
-	
-#ifdef MODULE
-	for(timeout = jiffies + 10*HZ, i=maxtim_data; timeout > jiffies; )
-	{
-		for ( ;i!=0;i--)
-		{
-			j=inb(CDi_status);
-			if (!(j&s_not_data_ready)) return (j);
-			if (!(j&s_not_result_ready)) return (j);
-			if (fam0L_drive) if (j&s_attention) return (j);
-		}
-		sbp_sleep(1);
-		i = 1;
-	}
-#else
-	if (current == task[0])
-		for(i=maxtim16;i!=0;i--)
-		{
-			j=inb(CDi_status);
-			if (!(j&s_not_data_ready)) return (j);
-			if (!(j&s_not_result_ready)) return (j);
-			if (fam0L_drive) if (j&s_attention) return (j);
-		}
-	else
-		for(timeout = jiffies + 10*HZ, i=maxtim_data; timeout > jiffies; )
-		{
-			for ( ;i!=0;i--)
-			{
-				j=inb(CDi_status);
-				if (!(j&s_not_data_ready)) return (j);
-				if (!(j&s_not_result_ready)) return (j);
-				if (fam0L_drive) if (j&s_attention) return (j);
-			}
-			sbp_sleep(1);
-			i = 1;
-		}
-#endif MODULE
-	msg(DBG_LCS,"CDi_stat_loop failed\n");
-	return (-1);
-}
-/*==========================================================================*/
-#if 00000
-/*==========================================================================*/
-static int tst_DataReady(void)
-{
-	int i;
-	
-	i=inb(CDi_status);
-	if (i&s_not_data_ready) return (0);
-	return (1);
-}
-/*==========================================================================*/
-static int tst_ResultReady(void)
-{
-	int i;
-	
-	i=inb(CDi_status);
-	if (i&s_not_result_ready) return (0);
-	return (1);
-}
-/*==========================================================================*/
-static int tst_Attention(void)
-{
-	int i;
-	
-	i=inb(CDi_status);
-	if (i&s_attention) return (1);
-	return (0);
-}
-/*==========================================================================*/
-#endif 00000
-/*==========================================================================*/
-static int ResponseInfo(void)
-{
-	int i,j,st=0;
-	u_long timeout;
-	
-#ifdef MODULE
-	if (0)
-#else
-	if (current == task[0])
-#endif MODULE
-		for (i=0;i<response_count;i++)
-		{
-			for (j=maxtim_8;j!=0;j--)
-			{
-				st=inb(CDi_status);
-				if (!(st&s_not_result_ready)) break;
-			}
-			if (j==0) 
-			{
-				msg(DBG_SEQ,"ResponseInfo: not_result_ready (got %d of %d bytes).\n", i, response_count);
-				break;
-			}
-			infobuf[i]=inb(CDi_info);
-		}
-	else 
-	{
-		for (i=0,timeout=jiffies+HZ;i<response_count;i++) 
-		{
-			for (j=maxtim_data; ; )
-			{
-				for ( ;j!=0;j-- )
-				{
-					st=inb(CDi_status);
-					if (!(st&s_not_result_ready)) break;
-				}
-				if ((j!=0)||(timeout<=jiffies)) break;
-				sbp_sleep(1);
-				j = 1;
-			}
-			if (timeout<=jiffies) break;
-			infobuf[i]=inb(CDi_info);
-		}
-	}
-#if 000
-	while (!(inb(CDi_status)&s_not_result_ready))
-	{
-		infobuf[i++]=inb(CDi_info);
-	}
-	j=i-response_count;
-	if (j>0) msg(DBG_INF,"ResponseInfo: got %d trailing bytes.\n",j);
-#endif 000
-	for (j=0;j<i;j++)
-		sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
-	msgbuf[j*3]=0;
-	msg(DBG_CMD,"ResponseInfo:%s (%d,%d)\n",msgbuf,response_count,i);
-	j=response_count-i;
-	if (j>0) return (-j);
-	else return (i);
-}
-/*==========================================================================*/
-static void EvaluateStatus(int st)
-{
-	D_S[d].status_bits=0;
-	if (fam1_drive) D_S[d].status_bits=st|p_success;
-	else if (fam0_drive)
-	{
-		if (st&p_caddin_old) D_S[d].status_bits |= p_door_closed|p_caddy_in;
-		if (st&p_spinning) D_S[d].status_bits |= p_spinning;
-		if (st&p_check) D_S[d].status_bits |= p_check;
- 		if (st&p_success_old) D_S[d].status_bits |= p_success;
- 		if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
-		if (st&p_disk_ok) D_S[d].status_bits |= p_disk_ok;
-	}
-	else if (famL_drive)
-	{
- 		D_S[d].status_bits |= p_success;
-		if (st&p_caddin_old) D_S[d].status_bits |= p_disk_ok|p_caddy_in;
-		if (st&p_spinning) D_S[d].status_bits |= p_spinning;
-		if (st&p_check) D_S[d].status_bits |= p_check;
-		if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
-		if (st&p_lcs_door_closed) D_S[d].status_bits |= p_door_closed;
-		if (st&p_lcs_door_locked) D_S[d].status_bits |= p_door_locked;
-	}
-	else if (fam2_drive)
-	{
- 		D_S[d].status_bits |= p_success;
-		if (st&p2_check) D_S[d].status_bits |= p1_check;
-		if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
-		if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
-		if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
-		if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
-		if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
-		if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
-		if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
-	}
-	else if (famT_drive)
-	{
-		return; /* still needs to get coded */
- 		D_S[d].status_bits |= p_success;
-		if (st&p2_check) D_S[d].status_bits |= p1_check;
-		if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
-		if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
-		if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
-		if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
-		if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
-		if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
-		if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
-	}
-	return;
-}
-/*==========================================================================*/
-static int get_state_T(void)
-{
-	int i;
-	
-	static int cmd_out_T(void);
-
-	msg(DBG_TE2,"doing get_state_T...\n");
-	clr_cmdbuf();
-	D_S[d].n_bytes=1;
-	drvcmd[0]=CMDT_STATUS;
-	i=cmd_out_T();
-	if (i>=0) i=infobuf[0];
-	else
-	{
-		msg(DBG_TEA,"get_state_T error %d\n", i);
-		return (i);
-	}
-	if (i>=0)
-		/* 2: closed, disk in */
-		D_S[d].status_bits=p1_door_closed|p1_disk_in|p1_spinning|p1_disk_ok;
-	else if (D_S[d].error_state==6)
-		/* 3: closed, disk in, changed ("06 xx xx") */
-		D_S[d].status_bits=p1_door_closed|p1_disk_in;
-	else if ((D_S[d].error_state!=2)||(D_S[d].b3!=0x3A)||(D_S[d].b4==0x00))
-	{
-		/* 1: closed, no disk ("xx yy zz"or "02 3A 00") */
-		D_S[d].status_bits=p1_door_closed;
-		D_S[d].open_count=0;
-	}
-	else if (D_S[d].b4==0x01)
-	{
-		/* 0: open ("02 3A 01") */
-		D_S[d].status_bits=0;
-		D_S[d].open_count=0;
-	}
-	else
-	{
-		/* 1: closed, no disk ("02 3A xx") */
-		D_S[d].status_bits=p1_door_closed;
-		D_S[d].open_count=0;
-	}
-	msg(DBG_TE2,"get_state_T done (%02X)...\n", D_S[d].status_bits);
-	return (D_S[d].status_bits);
-}
-/*==========================================================================*/
-static int ResponseStatus(void)
-{
-	int i,j;
-	u_long timeout;
-	
-	msg(DBG_STA,"doing ResponseStatus...\n");
-	if (famT_drive) return (get_state_T());
-#ifdef MODULE
-	if (0)
-#else
-	if (current == task[0])
-#endif MODULE
-	{
-		if (flags_cmd_out & f_respo3) j = maxtim_8;
-		else if (flags_cmd_out&f_respo2) j=maxtim16;
-		else j=maxtim04;
-		for (;j!=0;j--)
-		{
-			i=inb(CDi_status);
-			if (!(i&s_not_result_ready)) break;
-		}
-	}
-	else
-	{
-		if (flags_cmd_out & f_respo3) timeout = jiffies;
-		else if (flags_cmd_out & f_respo2) timeout = jiffies + 16*HZ;
-		else timeout = jiffies + 4*HZ;
-		j=maxtim_8;
-		do
-		{
-			for ( ;j!=0;j--)
-			{ 
-				i=inb(CDi_status);
-				if (!(i&s_not_result_ready)) break;
-			}
-			if ((j!=0)||(timeout<jiffies)) break;
-			sbp_sleep(1);
-			j = 1;
-		}
-		while (1);
-	}
-	if (j==0) 
-	{
-		if ((flags_cmd_out & f_respo3) == 0)
-			msg(DBG_STA,"ResponseStatus: timeout.\n");
-		D_S[d].status_bits=0;
-		return (-401);
-	}
-	i=inb(CDi_info);
-	msg(DBG_STA,"ResponseStatus: response %02X.\n", i);
-	EvaluateStatus(i);
-#if 0
-	if (fam0_drive)
-#endif
-		msg(DBG_STA,"status_bits=%02X, i=%02X\n",D_S[d].status_bits,i);
-#if 1
-	return (D_S[d].status_bits);
-#else
-	return (i);
-#endif 0
-}
-/*==========================================================================*/
-static void cc_ReadStatus(void)
-{
-	int i;
-	
-	msg(DBG_STA,"giving cc_ReadStatus command\n");
-	if (famT_drive) return;
-	SBPCD_CLI;
-	if (fam0L_drive) OUT(CDo_command,CMD0_STATUS);
-	else if (fam1_drive) OUT(CDo_command,CMD1_STATUS);
-	else if (fam2_drive) OUT(CDo_command,CMD2_STATUS);
-	if (!fam0L_drive) for (i=0;i<6;i++) OUT(CDo_command,0);
-	SBPCD_STI;
-}
-/*==========================================================================*/
-static int cc_ReadError(void)
-{
-	int i;
-
-	clr_cmdbuf();
-	msg(DBG_ERR,"giving cc_ReadError command.\n");
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_READ_ERR;
-		response_count=8;
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (fam0L_drive)
-	{
-		drvcmd[0]=CMD0_READ_ERR;
-		response_count=6;
-		if (famL_drive)
-			flags_cmd_out=f_putcmd;
-		else
-			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_READ_ERR;
-		response_count=6;
-		flags_cmd_out=f_putcmd;
-	}
-	else if (famT_drive)
-	{
-		response_count=5;
-		drvcmd[0]=CMDT_READ_ERR;
-	}
-	i=cmd_out();
-	D_S[d].error_byte=0;
-	msg(DBG_ERR,"cc_ReadError: cmd_out(CMDx_READ_ERR) returns %d (%02X)\n",i,i);
-	if (i<0) return (i);
-	if (fam0_drive) i=1;
-	else i=2;
-	D_S[d].error_byte=infobuf[i];
-	msg(DBG_ERR,"cc_ReadError: infobuf[%d] is %d (%02X)\n",i,D_S[d].error_byte,D_S[d].error_byte);
-	i=sta2err(infobuf[i]);
-	return (i);
-}
-/*==========================================================================*/
-static int cmd_out_T(void)
-{
-#undef CMDT_TRIES
-#define CMDT_TRIES 1000
-	
-	static int cc_DriveReset(void);
-	int i, j, l, ntries;
-	
-	D_S[d].error_state=0;
-	D_S[d].b3=0;
-	D_S[d].b4=0;
-	D_S[d].f_drv_error=0;
-	for (i=0;i<10;i++) sprintf(&msgbuf[i*3]," %02X",drvcmd[i]);
-	msgbuf[i*3]=0;
-	msg(DBG_CMD,"cmd_out_T:%s\n",msgbuf);
-
-	OUT(CDo_sel_i_d,0);
-	OUT(CDo_enable,D_S[d].drv_sel);
-	i=inb(CDi_status);
-	if (!(i&s_not_result_ready))
-	do
-	{
-		j=inb(CDi_info);
-		i=inb(CDi_status);
-		sbp_sleep(0);
-		msg(DBG_TEA,"cmd_out_T: spurious !s_not_result_ready. (%02X)\n", j);
-	}
-	while (!(i&s_not_result_ready));
-	cli();
-	for (i=0;i<10;i++) OUT(CDo_command,drvcmd[i]);
-	sti();
-	for (ntries=CMDT_TRIES;ntries>0;ntries--)
-	{
-		if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ);
-#if 1
-		OUT(CDo_sel_i_d,0);
-#endif
-		i=inb(CDi_status);
-		if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */
-		{
-			OUT(CDo_sel_i_d,1);
-			if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
-			if (drvcmd[0]==CMDT_DISKINFO)
-			{
-				l=0;
-				do
-				{
-					infobuf[l++]=inb(CDi_data);
-					i=inb(CDi_status);
-				}
-				while (!(i&s_not_data_ready));
-				for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
-				msgbuf[j*3]=0;
-				msg(DBG_CMD,"cmd_out_T data response:%s\n", msgbuf);
-			}
-			else
-			{
-				msg(DBG_TEA,"cmd_out_T: data response with cmd_%02X !!!!!!!!!!!!!!!!!!!!\n", drvcmd[0]);
-				j=0;
-				do
-				{
-					i=inb(CDi_data);
-					j++;
-					i=inb(CDi_status);
-				}
-				while (!(i&s_not_data_ready));
-				msg(DBG_TEA,"cmd_out_T: data response: discarded %d bytes.\n", j);
-				fatal_err++;
-			}
-		}
-		i=inb(CDi_status);
-		if (!(i&s_not_result_ready))
-		{
-			OUT(CDo_sel_i_d,0);
-			l=0;
-			do
-			{
-				infobuf[l++]=inb(CDi_info);
-				i=inb(CDi_status);
-			}
-			while (!(i&s_not_result_ready));
-			for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
-			msgbuf[j*3]=0;
-			msg(DBG_CMD,"cmd_out_T info response:%s\n", msgbuf);
-			if (infobuf[0]!=0x02) return (l); /* info length */
-			do
-			{
-				++recursion;
-				if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (%02X): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", drvcmd[0], recursion);
-				clr_cmdbuf();
-				drvcmd[0]=CMDT_READ_ERR;
-				j=cmd_out_T(); /* !!! recursive here !!! */
-				--recursion;
-				sbp_sleep(1);
-			}
-			while (j<0);
-			D_S[d].error_state=infobuf[2];
-			D_S[d].b3=infobuf[3];
-			D_S[d].b4=infobuf[4];
-			if (D_S[d].f_drv_error)
-			{
-				D_S[d].f_drv_error=0;
-				cc_DriveReset();
-				D_S[d].error_state=2;
-			}
-			return (-D_S[d].error_state-400);
-		}
-		if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
-		sbp_sleep(HZ/10);
-		if (ntries>(CMDT_TRIES-50)) continue;
-		msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1);
-	}
-	D_S[d].f_drv_error=1;
-	cc_DriveReset();
-	D_S[d].error_state=2;
-	return (-99);
-}
-/*==========================================================================*/
-static int cmd_out(void)
-{
-	int i=0;
-	
-	if (famT_drive) return(cmd_out_T());
-	
-	if (flags_cmd_out&f_putcmd)
-	{ 
-		for (i=0;i<7;i++)
-			sprintf(&msgbuf[i*3], " %02X", drvcmd[i]);
-		msgbuf[i*3]=0;
-		msg(DBG_CMD,"cmd_out:%s\n", msgbuf);
-		cli();
-		for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
-		sti();
-	}
-	if (response_count!=0)
-	{
-		if (cmd_type!=0)
-		{
-			if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-			msg(DBG_INF,"misleaded to try ResponseData.\n");
-			if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-			return (-22);
-		}
-		else i=ResponseInfo();
-		if (i<0) return (i);
-	}
-	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to CDi_stat_loop.\n");
-	if (flags_cmd_out&f_lopsta)
-	{
-		i=CDi_stat_loop();
-		if ((i<0)||!(i&s_attention)) return (-8);
-	}
-	if (!(flags_cmd_out&f_getsta)) goto LOC_229;
-	
- LOC_228:
-	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadStatus.\n");
-	cc_ReadStatus();
-	
- LOC_229:
-	if (flags_cmd_out&f_ResponseStatus) 
-	{
-		if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to ResponseStatus.\n");
-		i=ResponseStatus();
-		/* builds status_bits, returns orig. status or p_busy_new */
-		if (i<0) return (i);
-		if (flags_cmd_out&(f_bit1|f_wait_if_busy))
-		{
-			if (!st_check)
-			{
-				if ((flags_cmd_out&f_bit1)&&(i&p_success)) goto LOC_232;
-				if ((!(flags_cmd_out&f_wait_if_busy))||(!st_busy)) goto LOC_228;
-			}
-		}
-	}
- LOC_232:
-	if (!(flags_cmd_out&f_obey_p_check)) return (0);
-	if (!st_check) return (0);
-	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadError.\n");
-	i=cc_ReadError();
-	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cmd_out OK.\n");
-	msg(DBG_000,"cmd_out: cc_ReadError=%d\n", i);
-	return (i);
-}
-/*==========================================================================*/
-static int cc_Seek(u_int pos, char f_blk_msf)
-{
-	int i;
-	
-  clr_cmdbuf();
-	if (f_blk_msf>1) return (-3);
-	if (fam0_drive)
-	{
-		drvcmd[0]=CMD0_SEEK;
-		if (f_blk_msf==1) pos=msf2blk(pos);
-		drvcmd[2]=(pos>>16)&0x00FF;
-		drvcmd[3]=(pos>>8)&0x00FF;
-		drvcmd[4]=pos&0x00FF;
-		flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
-			f_ResponseStatus | f_obey_p_check | f_bit1;
-	}
-	else if (fam1L_drive)
-	{
-		drvcmd[0]=CMD1_SEEK; /* same as CMD1_ and CMDL_ */
-		if (f_blk_msf==0) pos=blk2msf(pos);
-		drvcmd[1]=(pos>>16)&0x00FF;
-		drvcmd[2]=(pos>>8)&0x00FF;
-		drvcmd[3]=pos&0x00FF;
-		if (famL_drive)
-			flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
-		else
-			flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_SEEK;
-		if (f_blk_msf==0) pos=blk2msf(pos);
-		drvcmd[2]=(pos>>24)&0x00FF;
-		drvcmd[3]=(pos>>16)&0x00FF;
-		drvcmd[4]=(pos>>8)&0x00FF;
-		drvcmd[5]=pos&0x00FF;
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (famT_drive)
-	{
-		drvcmd[0]=CMDT_SEEK;
-		if (f_blk_msf==1) pos=msf2blk(pos);
-		drvcmd[2]=(pos>>24)&0x00FF;
-		drvcmd[3]=(pos>>16)&0x00FF;
-		drvcmd[4]=(pos>>8)&0x00FF;
-		drvcmd[5]=pos&0x00FF;
-		D_S[d].n_bytes=1;
-	}
-	response_count=0;
-	i=cmd_out();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_SpinUp(void)
-{
-	int i;
-	
-	msg(DBG_SPI,"SpinUp.\n");
-	D_S[d].in_SpinUp = 1;
-	clr_cmdbuf();
-	if (fam0L_drive)
-	{
-		drvcmd[0]=CMD0_SPINUP;
-		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
-			f_ResponseStatus|f_obey_p_check|f_bit1;
-	}
-	else if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_SPINUP;
-		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_TRAY_CTL;
-		drvcmd[4]=0x01; /* "spinup" */
-		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (famT_drive)
-	{
-		drvcmd[0]=CMDT_TRAY_CTL;
-		drvcmd[4]=0x03; /* "insert", it hopefully spins the drive up */
-	}
-	response_count=0;
-	i=cmd_out();
-	D_S[d].in_SpinUp = 0;
-	return (i);
-}
-/*==========================================================================*/
-static int cc_SpinDown(void)
-{
-	int i;
-	
-	if (fam0_drive) return (0);
-	clr_cmdbuf();
-	response_count=0;
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_SPINDOWN;
-		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_TRAY_CTL;
-		drvcmd[4]=0x02; /* "eject" */
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (famL_drive)
-	{
-		drvcmd[0]=CMDL_SPINDOWN;
-		drvcmd[1]=1;
-		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
-	}
-	else if (famT_drive)
-	{
-		drvcmd[0]=CMDT_TRAY_CTL;
-		drvcmd[4]=0x02; /* "eject" */
-	}
-	i=cmd_out();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_get_mode_T(void)
-{
-	int i;
-	
-	clr_cmdbuf();
-	response_count=10;
-	drvcmd[0]=CMDT_GETMODE;
-	drvcmd[4]=response_count;
-	i=cmd_out_T();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_set_mode_T(void)
-{
-	int i;
-	
-	clr_cmdbuf();
-	response_count=1;
-	drvcmd[0]=CMDT_SETMODE;
-	drvcmd[1]=D_S[d].speed_byte;
-	drvcmd[2]=D_S[d].frmsiz>>8;
-	drvcmd[3]=D_S[d].frmsiz&0x0FF;
-	drvcmd[4]=D_S[d].f_XA; /* 1: XA */
-	drvcmd[5]=D_S[d].type_byte; /* 0, 1, 3 */
-	drvcmd[6]=D_S[d].mode_xb_6;
-	drvcmd[7]=D_S[d].mode_yb_7|D_S[d].volume_control;
-	drvcmd[8]=D_S[d].mode_xb_8;
-	drvcmd[9]=D_S[d].delay;
-	i=cmd_out_T();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_prep_mode_T(void)
-{
-	int i, j;
-	
-	i=cc_get_mode_T();
-	if (i<0) return (i);
-	for (i=0;i<10;i++)
-		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-	msgbuf[i*3]=0;
-	msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
-	D_S[d].speed_byte=0x02; /* 0x02: auto quad, 0x82: quad, 0x81: double, 0x80: single */
-	D_S[d].frmsiz=make16(infobuf[2],infobuf[3]);
-	D_S[d].f_XA=infobuf[4];
-	if (D_S[d].f_XA==0) D_S[d].type_byte=0;
-	else D_S[d].type_byte=1;
-	D_S[d].mode_xb_6=infobuf[6];
-	D_S[d].mode_yb_7=1;
-	D_S[d].mode_xb_8=infobuf[8];
-	D_S[d].delay=0; /* 0, 1, 2, 3 */
-	j=cc_set_mode_T();
-	i=cc_get_mode_T();
-	for (i=0;i<10;i++)
-		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-	msgbuf[i*3]=0;
-	msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
-	return (j);
-}
-/*==========================================================================*/
-static int cc_SetSpeed(u_char speed, u_char x1, u_char x2)
-{
-	int i;
-	
-	if (fam0L_drive) return (-3);
-	clr_cmdbuf();
-	response_count=0;
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_SETMODE;
-		drvcmd[1]=0x03;
-		drvcmd[2]=speed;
-		drvcmd[3]=x1;
-		drvcmd[4]=x2;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_SETSPEED;
-		if (speed&speed_auto)
-		{
-			drvcmd[2]=0xFF;
-			drvcmd[3]=0xFF;
-		}
-		else
-		{
-			drvcmd[2]=0;
-			drvcmd[3]=150;
-		}
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (famT_drive)
-	{
-		return (0);
-	}
-	i=cmd_out();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_SetVolume(void)
-{
-	int i;
-	u_char channel0,channel1,volume0,volume1;
-	u_char control0,value0,control1,value1;
-	
-	D_S[d].diskstate_flags &= ~volume_bit;
-	clr_cmdbuf();
-	channel0=D_S[d].vol_chan0;
-	volume0=D_S[d].vol_ctrl0;
-	channel1=control1=D_S[d].vol_chan1;
-	volume1=value1=D_S[d].vol_ctrl1;
-	control0=value0=0;
-	
-	if (((D_S[d].drv_options&audio_mono)!=0)&&(D_S[d].drv_type>=drv_211))
-	{
-		if ((volume0!=0)&&(volume1==0))
-		{
-			volume1=volume0;
-			channel1=channel0;
-		}
-		else if ((volume0==0)&&(volume1!=0))
-		{
-			volume0=volume1;
-			channel0=channel1;
-		}
-	}
-	if (channel0>1)
-	{
-		channel0=0;
-		volume0=0;
-	}
-	if (channel1>1)
-	{
-		channel1=1;
-		volume1=0;
-	}
-	
-	if (fam1_drive)
-	{
-		control0=channel0+1;
-		control1=channel1+1;
-		value0=(volume0>volume1)?volume0:volume1;
-		value1=value0;
-		if (volume0==0) control0=0;
-		if (volume1==0) control1=0;
-		drvcmd[0]=CMD1_SETMODE;
-		drvcmd[1]=0x05;
-		drvcmd[3]=control0;
-		drvcmd[4]=value0;
-		drvcmd[5]=control1;
-		drvcmd[6]=value1;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		control0=channel0+1;
-		control1=channel1+1;
-		value0=(volume0>volume1)?volume0:volume1;
-		value1=value0;
-		if (volume0==0) control0=0;
-		if (volume1==0) control1=0;
-		drvcmd[0]=CMD2_SETMODE;
-		drvcmd[1]=0x0E;
-		drvcmd[3]=control0;
-		drvcmd[4]=value0;
-		drvcmd[5]=control1;
-		drvcmd[6]=value1;
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (famL_drive)
-	{
-		if ((volume0==0)||(channel0!=0)) control0 |= 0x80;
-		if ((volume1==0)||(channel1!=1)) control0 |= 0x40;
-		if (volume0|volume1) value0=0x80;
-		drvcmd[0]=CMDL_SETMODE;
-		drvcmd[1]=0x03;
-		drvcmd[4]=control0;
-		drvcmd[5]=value0;
-		flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
-	}
-	else if (fam0_drive) /* different firmware levels */
-	{
-		if (D_S[d].drv_type>=drv_300)
-		{
-			control0=volume0&0xFC;
-			value0=volume1&0xFC;
-			if ((volume0!=0)&&(volume0<4)) control0 |= 0x04;
-			if ((volume1!=0)&&(volume1<4)) value0 |= 0x04;
-			if (channel0!=0) control0 |= 0x01;
-			if (channel1==1) value0 |= 0x01;
-		}
-		else
-		{
-			value0=(volume0>volume1)?volume0:volume1;
-			if (D_S[d].drv_type<drv_211)
-			{
-				if (channel0!=0)
-				{
-					i=channel1;
-					channel1=channel0;
-					channel0=i;
-					i=volume1;
-					volume1=volume0;
-					volume0=i;
-				}
-				if (channel0==channel1)
-				{
-					if (channel0==0)
-					{
-						channel1=1;
-						volume1=0;
-						volume0=value0;
-					}
-					else
-					{
-						channel0=0;
-						volume0=0;
-						volume1=value0;
-					}
-				}
-			}
-			
-			if ((volume0!=0)&&(volume1!=0))
-			{
-				if (volume0==0xFF) volume1=0xFF;
-				else if (volume1==0xFF) volume0=0xFF;
-			}
-			else if (D_S[d].drv_type<drv_201) volume0=volume1=value0;
-			
-			if (D_S[d].drv_type>=drv_201)
-			{
-				if (volume0==0) control0 |= 0x80;
-				if (volume1==0) control0 |= 0x40;
-			}
-			if (D_S[d].drv_type>=drv_211)
-			{
-				if (channel0!=0) control0 |= 0x20;
-				if (channel1!=1) control0 |= 0x10;
-			}
-		}
-		drvcmd[0]=CMD0_SETMODE;
-		drvcmd[1]=0x83;
-		drvcmd[4]=control0;
-		drvcmd[5]=value0;
-		flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (famT_drive)
-	{
-		D_S[d].volume_control=0;
-		if (!volume0) D_S[d].volume_control|=0x10;
-		if (!volume1) D_S[d].volume_control|=0x20;
-		i=cc_prep_mode_T();
-		if (i<0) return (i);
-	}
-	if (!famT_drive)
-	{
-		response_count=0;
-		i=cmd_out();
-		if (i<0) return (i);
-	}
-	D_S[d].diskstate_flags |= volume_bit;
-	return (0);
-}
-/*==========================================================================*/
-static int GetStatus(void)
-{
-	int i;
-	
-	if (famT_drive) return (0);
-	flags_cmd_out=f_getsta|f_ResponseStatus|f_obey_p_check;
-	response_count=0;
-	cmd_type=0;
-	i=cmd_out();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_DriveReset(void)
-{
-	int i;
-	
-	msg(DBG_RES,"cc_DriveReset called.\n");
-	clr_cmdbuf();
-	response_count=0;
-	if (fam0L_drive) OUT(CDo_reset,0x00);
-	else if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_RESET;
-		flags_cmd_out=f_putcmd;
-		i=cmd_out();
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_RESET;
-		flags_cmd_out=f_putcmd;
-		i=cmd_out();
-		OUT(CDo_reset,0x00);
-	}
-	else if (famT_drive)
-	{
-		OUT(CDo_sel_i_d,0);
-		OUT(CDo_enable,D_S[d].drv_sel);
-		OUT(CDo_command,CMDT_RESET);
-		for (i=1;i<10;i++) OUT(CDo_command,0);
-	}
-	if (fam0L_drive) sbp_sleep(5*HZ); /* wait 5 seconds */
-	else sbp_sleep(1*HZ); /* wait a second */
-#if 1
-	if (famT_drive)
-	{
-		msg(DBG_TEA, "================CMDT_RESET given=================.\n");
-		sbp_sleep(3*HZ);
-	}
-#endif 1
-	flush_status();
-	i=GetStatus();
-	if (i<0) return i;
-	if (!famT_drive)
-		if (D_S[d].error_byte!=aud_12) return -501;
-	return (0);
-}
-/*==========================================================================*/
-static int SetSpeed(void)
-{
-	int i, speed;
-	
-	if (!(D_S[d].drv_options&(speed_auto|speed_300|speed_150))) return (0);
-	speed=speed_auto;
-	if (!(D_S[d].drv_options&speed_auto))
-	{
-		speed |= speed_300;
-		if (!(D_S[d].drv_options&speed_300)) speed=0;
-	}
-	i=cc_SetSpeed(speed,0,0);
-	return (i);
-}
-/*==========================================================================*/
-static int DriveReset(void)
-{
-	int i;
-	
-	i=cc_DriveReset();
-	if (i<0) return (-22);
-	do
-	{
-		i=GetStatus();
-		if ((i<0)&&(i!=-615)) return (-2); /* i!=-615 is from sta2err */
-		if (!st_caddy_in) break;
-		sbp_sleep(1);
-	}
-	while (!st_diskok);
-#if 000
-	D_S[d].CD_changed=1;
-#endif
-	if ((st_door_closed) && (st_caddy_in))
-	{
-		i=DiskInfo();
-		if (i<0) return (-23);
-	}
-	return (0);
-}
-/*==========================================================================*/
-static int cc_PlayAudio(int pos_audio_start,int pos_audio_end)
-{
-	int i, j, n;
-	
-	if (D_S[d].audio_state==audio_playing) return (-EINVAL);
-	clr_cmdbuf();
-	response_count=0;
-	if (famL_drive)
-	{
-		drvcmd[0]=CMDL_PLAY;
-		i=msf2blk(pos_audio_start);
-		n=msf2blk(pos_audio_end)+1-i;
-		drvcmd[1]=(i>>16)&0x00FF;
-		drvcmd[2]=(i>>8)&0x00FF;
-		drvcmd[3]=i&0x00FF;
-		drvcmd[4]=(n>>16)&0x00FF;
-		drvcmd[5]=(n>>8)&0x00FF;
-		drvcmd[6]=n&0x00FF;
-		flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
-			f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
-	}
-	else
-	{
-		j=1;
-		if (fam1_drive)
-		{
-			drvcmd[0]=CMD1_PLAY_MSF;
-			flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus |
-				f_obey_p_check | f_wait_if_busy;
-		}
-		else if (fam2_drive)
-		{
-			drvcmd[0]=CMD2_PLAY_MSF;
-			flags_cmd_out = f_putcmd | f_ResponseStatus;
-		}
-		else if (famT_drive)
-		{
-			drvcmd[0]=CMDT_PLAY_MSF;
-			j=3;
-			response_count=1;
-		}
-		else if (fam0_drive)
-		{
-			drvcmd[0]=CMD0_PLAY_MSF;
-			flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
-				f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
-		}
-		drvcmd[j]=(pos_audio_start>>16)&0x00FF;
-		drvcmd[j+1]=(pos_audio_start>>8)&0x00FF;
-		drvcmd[j+2]=pos_audio_start&0x00FF;
-		drvcmd[j+3]=(pos_audio_end>>16)&0x00FF;
-		drvcmd[j+4]=(pos_audio_end>>8)&0x00FF;
-		drvcmd[j+5]=pos_audio_end&0x00FF;
-	}
-	i=cmd_out();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_Pause_Resume(int pau_res)
-{
-	int i;
-	
-	clr_cmdbuf();
-	response_count=0;
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_PAU_RES;
-		if (pau_res!=1) drvcmd[1]=0x80;
-		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_PAU_RES;
-		if (pau_res!=1) drvcmd[2]=0x01;
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (fam0L_drive)
-	{
-		drvcmd[0]=CMD0_PAU_RES;
-		if (pau_res!=1) drvcmd[1]=0x80;
-		if (famL_drive)
-			flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
-				f_obey_p_check|f_bit1;
-		else
-			flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
-				f_obey_p_check;
-	}
-	else if (famT_drive)
-	{
-		if (pau_res==3)	return (cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end));
-		else if (pau_res==1) drvcmd[0]=CMDT_PAUSE;
-		else return (-56);
-	}
-	i=cmd_out();
-	return (i);
-}
-/*==========================================================================*/
-static int cc_LockDoor(char lock)
-{
-	int i;
-	
-	if (fam0_drive) return (0);
-	msg(DBG_LCK,"cc_LockDoor: %d (drive %d)\n", lock, d);
-	msg(DBG_LCS,"p_door_locked bit %d before\n", st_door_locked);
-	clr_cmdbuf();
-	response_count=0;
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_LOCK_CTL;
-		if (lock==1) drvcmd[1]=0x01;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_LOCK_CTL;
-		if (lock==1) drvcmd[4]=0x01;
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (famL_drive)
-	{
-		drvcmd[0]=CMDL_LOCK_CTL;
-		if (lock==1) drvcmd[1]=0x01;
-		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
-	}
-	else if (famT_drive)
-	{
-		drvcmd[0]=CMDT_LOCK_CTL;
-		if (lock==1) drvcmd[4]=0x01;
-	}
-	i=cmd_out();
-	msg(DBG_LCS,"p_door_locked bit %d after\n", st_door_locked);
-	return (i);
-}
-/*==========================================================================*/
-/*==========================================================================*/
-static int UnLockDoor(void)
-{
-	int i,j;
-	
-	j=20;
-	do
-	{
-		i=cc_LockDoor(0);
-		--j;
-		sbp_sleep(1);
-	}
-	while ((i<0)&&(j));
-	if (i<0)
-	{
-		cc_DriveReset();
-		return -84;
-	}
-	return (0);
-}
-/*==========================================================================*/
-static int LockDoor(void)
-{
-	int i,j;
-	
-	j=20;
-	do
-	{
-		i=cc_LockDoor(1);
-		--j;
-		sbp_sleep(1);
-	}
-	while ((i<0)&&(j));
-	if (j==0)
-	{		
-		cc_DriveReset();
-		j=20;
-		do
-		{
-			i=cc_LockDoor(1);
-			--j;
-			sbp_sleep(1);
-		}
-		while ((i<0)&&(j));
-	}
-	return (i);
-}
-/*==========================================================================*/
-static int cc_CloseTray(void)
-{
-	int i;
-	
-	if (fam0_drive) return (0);
-	msg(DBG_LCK,"cc_CloseTray (drive %d)\n", d);
-	msg(DBG_LCS,"p_door_closed bit %d before\n", st_door_closed);
-	
-	clr_cmdbuf();
-	response_count=0;
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_TRAY_CTL;
-		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_TRAY_CTL;
-		drvcmd[1]=0x01;
-		drvcmd[4]=0x03; /* "insert" */
-		flags_cmd_out=f_putcmd|f_ResponseStatus;
-	}
-	else if (famL_drive)
-	{
-		drvcmd[0]=CMDL_TRAY_CTL;
-		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
-			f_ResponseStatus|f_obey_p_check|f_bit1;
-	}
-	else if (famT_drive)
-	{
-		drvcmd[0]=CMDT_TRAY_CTL;
-		drvcmd[4]=0x03; /* "insert" */
-	}
-	i=cmd_out();
-	msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed);
-	return (i);
-}
-/*==========================================================================*/
-static int cc_ReadSubQ(void)
-{
-	int i,j;
-
-	D_S[d].diskstate_flags &= ~subq_bit;
-	for (j=255;j>0;j--)
-	{
-		clr_cmdbuf();
-		if (fam1_drive)
-		{
-			drvcmd[0]=CMD1_READSUBQ;
-			flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-			response_count=11;
-		}
-		else if (fam2_drive)
-		{
-			drvcmd[0]=CMD2_READSUBQ;
-			drvcmd[1]=0x02;
-			drvcmd[3]=0x01;
-			flags_cmd_out=f_putcmd;
-			response_count=10;
-		}
-		else if (fam0L_drive)
-		{
-			drvcmd[0]=CMD0_READSUBQ;
-			drvcmd[1]=0x02;
-			if (famL_drive)
-				flags_cmd_out=f_putcmd;
-			else
-				flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-			response_count=13;
-		}
-		else if (famT_drive)
-		{
-			response_count=12;
-			drvcmd[0]=CMDT_READSUBQ;
-			drvcmd[1]=0x02;
-			drvcmd[2]=0x40;
-			drvcmd[3]=0x01;
-			drvcmd[8]=response_count;
-		}
-		i=cmd_out();
-		if (i<0) return (i);
-		for (i=0;i<response_count;i++)
-		{
-			sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-			msgbuf[i*3]=0;
-			msg(DBG_SQ1,"cc_ReadSubQ:%s\n", msgbuf);
-		}
-		if (famT_drive) break;
-		if (infobuf[0]!=0) break;
-		if ((!st_spinning) || (j==1))
-		{
-			D_S[d].SubQ_ctl_adr=D_S[d].SubQ_trk=D_S[d].SubQ_pnt_idx=D_S[d].SubQ_whatisthis=0;
-			D_S[d].SubQ_run_tot=D_S[d].SubQ_run_trk=0;
-			return (0);
-		}
-	}
-	if (famT_drive) D_S[d].SubQ_ctl_adr=infobuf[1];
-	else D_S[d].SubQ_ctl_adr=swap_nibbles(infobuf[1]);
-	D_S[d].SubQ_trk=byt2bcd(infobuf[2]);
-	D_S[d].SubQ_pnt_idx=byt2bcd(infobuf[3]);
-	if (fam0L_drive) i=5;
-	else if (fam12_drive) i=4;
-	else if (famT_drive) i=8;
-	D_S[d].SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
-	i=7;
-	if (fam0L_drive) i=9;
-	else if (fam12_drive) i=7;
-	else if (famT_drive) i=4;
-	D_S[d].SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
-	D_S[d].SubQ_whatisthis=infobuf[i+3];
-	D_S[d].diskstate_flags |= subq_bit;
-	return (0);
-}
-/*==========================================================================*/
-static int cc_ModeSense(void)
-{
-	int i;
-	
-	if (fam2_drive) return (0);
-	D_S[d].diskstate_flags &= ~frame_size_bit;
-	clr_cmdbuf();
-	if (fam1_drive)
-	{
-		response_count=5;
-		drvcmd[0]=CMD1_GETMODE;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam0L_drive)
-	{
-		response_count=2;
-		drvcmd[0]=CMD0_GETMODE;
-		if (famL_drive) flags_cmd_out=f_putcmd;
-		else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (famT_drive)
-	{
-		response_count=10;
-		drvcmd[0]=CMDT_GETMODE;
-		drvcmd[4]=response_count;
-	}
-	i=cmd_out();
-	if (i<0) return (i);
-	i=0;
-	D_S[d].sense_byte=0;
-	if (fam1_drive) D_S[d].sense_byte=infobuf[i++];
-	else if (famT_drive)
-	{
-		if (infobuf[4]==0x01) D_S[d].xa_byte=0x20;
-		else D_S[d].xa_byte=0;
-		i=2;
-	}
-	D_S[d].frame_size=make16(infobuf[i],infobuf[i+1]);
-	for (i=0;i<response_count;i++)
-		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-	msgbuf[i*3]=0;
-	msg(DBG_XA1,"cc_ModeSense:%s\n", msgbuf);
-	
-	D_S[d].diskstate_flags |= frame_size_bit;
-	return (0);
-}
-/*==========================================================================*/
-/*==========================================================================*/
-static int cc_ModeSelect(int framesize)
-{
-	int i;
-	
-	if (fam2_drive) return (0);
-	D_S[d].diskstate_flags &= ~frame_size_bit;
-	clr_cmdbuf();
-	D_S[d].frame_size=framesize;
-	if (framesize==CD_FRAMESIZE_RAW) D_S[d].sense_byte=0x82;
-	else D_S[d].sense_byte=0x00;
-	
-	msg(DBG_XA1,"cc_ModeSelect: %02X %04X\n",
-	    D_S[d].sense_byte, D_S[d].frame_size);
-	
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_SETMODE;
-		drvcmd[1]=0x00;
-		drvcmd[2]=D_S[d].sense_byte;
-		drvcmd[3]=(D_S[d].frame_size>>8)&0xFF;
-		drvcmd[4]=D_S[d].frame_size&0xFF;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam0L_drive)
-	{
-		drvcmd[0]=CMD0_SETMODE;
-		drvcmd[1]=0x00;
-		drvcmd[2]=(D_S[d].frame_size>>8)&0xFF;
-		drvcmd[3]=D_S[d].frame_size&0xFF;
-		drvcmd[4]=0x00;
-		if(famL_drive)
-			flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check;
-		else
-			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (famT_drive)
-	{
-		return (-1);
-	}
-	response_count=0;
-	i=cmd_out();
-	if (i<0) return (i);
-	D_S[d].diskstate_flags |= frame_size_bit;
-	return (0);
-}
-/*==========================================================================*/
-static int cc_GetVolume(void)
-{
-	int i;
-	u_char switches;
-	u_char chan0=0;
-	u_char vol0=0;
-	u_char chan1=1;
-	u_char vol1=0;
-	
-	D_S[d].diskstate_flags &= ~volume_bit;
-	clr_cmdbuf();
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_GETMODE;
-		drvcmd[1]=0x05;
-		response_count=5;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_GETMODE;
-		drvcmd[1]=0x0E;
-		response_count=5;
-		flags_cmd_out=f_putcmd;
-	}
-	else if (fam0L_drive)
-	{
-		drvcmd[0]=CMD0_GETMODE;
-		drvcmd[1]=0x03;
-		response_count=2;
-		if(famL_drive)
-			flags_cmd_out=f_putcmd;
-		else
-			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (famT_drive)
-	{
-		i=cc_get_mode_T();
-		if (i<0) return (i);
-	}
-	if (!famT_drive)
-	{
-		i=cmd_out();
-		if (i<0) return (i);
-	}
-	if (fam1_drive)
-	{
-		chan0=infobuf[1]&0x0F;
-		vol0=infobuf[2];
-		chan1=infobuf[3]&0x0F;
-		vol1=infobuf[4];
-		if (chan0==0)
-		{
-			chan0=1;
-			vol0=0;
-		}
-		if (chan1==0)
-		{
-			chan1=2;
-			vol1=0;
-		}
-		chan0 >>= 1;
-		chan1 >>= 1;
-	}
-	else if (fam2_drive)
-	{
-		chan0=infobuf[1];
-		vol0=infobuf[2];
-		chan1=infobuf[3];
-		vol1=infobuf[4];
-	}
-	else if (famL_drive)
-	{
-		chan0=0;
-		chan1=1;
-		vol0=vol1=infobuf[1];
-		switches=infobuf[0];
-		if ((switches&0x80)!=0) chan0=1;
-		if ((switches&0x40)!=0) chan1=0;
-	}
-	else if (fam0_drive) /* different firmware levels */
-	{
-		chan0=0;
-		chan1=1;
-		vol0=vol1=infobuf[1];
-		if (D_S[d].drv_type>=drv_201)
-		{
-			if (D_S[d].drv_type<drv_300)
-			{
-				switches=infobuf[0];
-				if ((switches&0x80)!=0) vol0=0;
-				if ((switches&0x40)!=0) vol1=0;
-				if (D_S[d].drv_type>=drv_211)
-				{
-					if ((switches&0x20)!=0) chan0=1;
-					if ((switches&0x10)!=0) chan1=0;
-				}
-			}
-			else
-			{
-				vol0=infobuf[0];
-				if ((vol0&0x01)!=0) chan0=1;
-				if ((vol1&0x01)==0) chan1=0;
-				vol0 &= 0xFC;
-				vol1 &= 0xFC;
-				if (vol0!=0) vol0 += 3;
-				if (vol1!=0) vol1 += 3;
-			}
-		}
-	}
-	else if (famT_drive)
-	{
-		D_S[d].volume_control=infobuf[7];
-		chan0=0;
-		chan1=1;
-		if (D_S[d].volume_control&0x10) vol0=0;
-		else vol0=0xff;
-		if (D_S[d].volume_control&0x20) vol1=0;
-		else vol1=0xff;
-	}
-	D_S[d].vol_chan0=chan0;
-	D_S[d].vol_ctrl0=vol0;
-	D_S[d].vol_chan1=chan1;
-	D_S[d].vol_ctrl1=vol1;
-#if 000
-	D_S[d].vol_chan2=2;
-	D_S[d].vol_ctrl2=0xFF;
-	D_S[d].vol_chan3=3;
-	D_S[d].vol_ctrl3=0xFF;
-#endif 000
-	D_S[d].diskstate_flags |= volume_bit;
-	return (0);
-}
-/*==========================================================================*/
-static int cc_ReadCapacity(void)
-{
-	int i, j;
-	
-	if (famL_drive) return (0); /* some firmware lacks this command */
-	if (famT_drive) return (0); /* done with cc_ReadTocDescr() */
-	D_S[d].diskstate_flags &= ~cd_size_bit;
-	for (j=3;j>0;j--)
-	{
-		clr_cmdbuf();
-		if (fam1_drive)
-		{
-			drvcmd[0]=CMD1_CAPACITY;
-			response_count=5;
-			flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-		}
-		else if (fam2_drive)
-		{
-			drvcmd[0]=CMD2_CAPACITY;
-			response_count=8;
-			flags_cmd_out=f_putcmd;
-		}
-		else if (fam0_drive)
-		{
-			drvcmd[0]=CMD0_CAPACITY;
-			response_count=5;
-			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-		}
-		i=cmd_out();
-		if (i>=0) break;
-		msg(DBG_000,"cc_ReadCapacity: cmd_out: err %d\n", i);
-		cc_ReadError();
-	}
-	if (j==0) return (i);
-	if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_MSF_OFFSET;
-	else if (fam0_drive) D_S[d].CDsize_frm=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
-	else if (fam2_drive) D_S[d].CDsize_frm=make32(make16(infobuf[0],infobuf[1]),make16(infobuf[2],infobuf[3]));
-	D_S[d].diskstate_flags |= cd_size_bit;
-	msg(DBG_000,"cc_ReadCapacity: %d frames.\n", D_S[d].CDsize_frm);
-	return (0);
-}
-/*==========================================================================*/
-static int cc_ReadTocDescr(void)
-{
-	int i;
-	
-	D_S[d].diskstate_flags &= ~toc_bit;
-	clr_cmdbuf();
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_DISKINFO;
-		response_count=6;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam0L_drive)
-	{
-		drvcmd[0]=CMD0_DISKINFO;
-		response_count=6;
-		if(famL_drive)
-			flags_cmd_out=f_putcmd;
-		else
-			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		/* possibly longer timeout periods necessary */
-		D_S[d].f_multisession=0;
-		drvcmd[0]=CMD2_DISKINFO;
-		drvcmd[1]=0x02;
-		drvcmd[2]=0xAB;
-		drvcmd[3]=0xFF; /* session */
-		response_count=8;
-		flags_cmd_out=f_putcmd;
-	}
-	else if (famT_drive)
-	{
-		D_S[d].f_multisession=0;
-		response_count=12;
-		drvcmd[0]=CMDT_DISKINFO;
-		drvcmd[1]=0x02;
-		drvcmd[6]=CDROM_LEADOUT;
-		drvcmd[8]=response_count;
-		drvcmd[9]=0x00;
-	}
-	i=cmd_out();
-	if (i<0) return (i);
-	if ((fam1_drive)||(fam2_drive)||(famL_drive)||(fam0_drive))
-		D_S[d].xa_byte=infobuf[0];
-	if (fam2_drive)
-	{
-		D_S[d].first_session=infobuf[1];
-		D_S[d].last_session=infobuf[2];
-		D_S[d].n_first_track=infobuf[3];
-		D_S[d].n_last_track=infobuf[4];
-		if (D_S[d].first_session!=D_S[d].last_session)
-		{
-			D_S[d].f_multisession=1;
-			D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7])));
-		}
-#if 0
-		if (D_S[d].first_session!=D_S[d].last_session)
-		{
-			if (D_S[d].last_session<=20)
-				zwanzig=D_S[d].last_session+1;
-			else zwanzig=20;
-			for (count=D_S[d].first_session;count<zwanzig;count++)
-			{
-				drvcmd[0]=CMD2_DISKINFO;
-				drvcmd[1]=0x02;
-				drvcmd[2]=0xAB;
-				drvcmd[3]=count;
-				response_count=8;
-				flags_cmd_out=f_putcmd;
-				i=cmd_out();
-				if (i<0) return (i);
-				D_S[d].msf_multi_n[count]=make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7]));
-			}
-			D_S[d].diskstate_flags |= multisession_bit;
-		}
-#endif
-		drvcmd[0]=CMD2_DISKINFO;
-		drvcmd[1]=0x02;
-		drvcmd[2]=0xAA;
-		drvcmd[3]=0xFF;
-		response_count=5;
-		flags_cmd_out=f_putcmd;
-		i=cmd_out();
-		if (i<0) return (i);
-		D_S[d].size_msf=make32(make16(0,infobuf[2]),make16(infobuf[3],infobuf[4]));
-		D_S[d].size_blk=msf2blk(D_S[d].size_msf);
-	}
-	else if (famT_drive)
-	{
-		D_S[d].size_msf=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
-		D_S[d].size_blk=msf2blk(D_S[d].size_msf);
-		D_S[d].CDsize_frm=D_S[d].size_blk+1;
-		D_S[d].n_first_track=infobuf[2];
-		D_S[d].n_last_track=infobuf[3];
-	}
-	else
-	{
-		D_S[d].n_first_track=infobuf[1];
-		D_S[d].n_last_track=infobuf[2];
-		D_S[d].size_msf=make32(make16(0,infobuf[3]),make16(infobuf[4],infobuf[5]));
-		D_S[d].size_blk=msf2blk(D_S[d].size_msf);
-		if (famL_drive) D_S[d].CDsize_frm=D_S[d].size_blk+1;
-	}
-	D_S[d].diskstate_flags |= toc_bit;
-	msg(DBG_TOC,"TocDesc: %02X %02X %02X %08X\n",
-	    D_S[d].xa_byte,
-	    D_S[d].n_first_track,
-	    D_S[d].n_last_track,
-	    D_S[d].size_msf);
-	return (0);
-}
-/*==========================================================================*/
-static int cc_ReadTocEntry(int num)
-{
-	int i;
-	
-	clr_cmdbuf();
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_READTOC;
-		drvcmd[2]=num;
-		response_count=8;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (fam2_drive)
-	{
-		/* possibly longer timeout periods necessary */
-		drvcmd[0]=CMD2_DISKINFO;
-		drvcmd[1]=0x02;
-		drvcmd[2]=num;
-		response_count=5;
-		flags_cmd_out=f_putcmd;
-	}
-	else if (fam0L_drive)
-	{
-		drvcmd[0]=CMD0_READTOC;
-		drvcmd[1]=0x02;
-		drvcmd[2]=num;
-		response_count=8;
-		if(famL_drive)
-			flags_cmd_out=f_putcmd;
-		else
-	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-	}
-	else if (famT_drive)
-	{
-		response_count=12;
-		drvcmd[0]=CMDT_DISKINFO;
-		drvcmd[1]=0x02;
-		drvcmd[6]=num;
-		drvcmd[8]=response_count;
-		drvcmd[9]=0x00;
-	}
-	i=cmd_out();
-	if (i<0) return (i);
-	if ((fam1_drive)||(famL_drive)||(fam0_drive))
-	{
-		D_S[d].TocEnt_nixbyte=infobuf[0];
-		i=1;
-	}
-	else if (fam2_drive) i=0;
-	else if (famT_drive)
-	{
-		i=5;
-	}
-	D_S[d].TocEnt_ctl_adr=swap_nibbles(infobuf[i++]);
-	if ((fam1_drive)||(famL_drive)||(fam0_drive))
-	{
-		D_S[d].TocEnt_number=infobuf[i++];
-		D_S[d].TocEnt_format=infobuf[i];
-	}
-	else D_S[d].TocEnt_number=num;
-	if (fam1_drive) i=4;
-	else if (fam0L_drive) i=5;
-	else if (fam2_drive) i=2;
-	else if (famT_drive) i=9;
-	D_S[d].TocEnt_address=make32(make16(0,infobuf[i]),
-				     make16(infobuf[i+1],infobuf[i+2]));
-	msg(DBG_TOC,"TocEntry: %02X %02X %02X %02X %08X\n",
-	    D_S[d].TocEnt_nixbyte, D_S[d].TocEnt_ctl_adr,
-	    D_S[d].TocEnt_number, D_S[d].TocEnt_format,
-	    D_S[d].TocEnt_address);
-	return (0);
-}
-/*==========================================================================*/
-static int cc_ReadPacket(void)
-{
-	int i;
-	
-	clr_cmdbuf();
-	drvcmd[0]=CMD0_PACKET;
-	drvcmd[1]=response_count;
-	if(famL_drive) flags_cmd_out=f_putcmd;
-	else if (fam01_drive)
-		flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
-	else if (fam2_drive) return (-1); /* not implemented yet */
-	else if (famT_drive)
-	{
-		return (-1);
-	}
-	i=cmd_out();
-	return (i);
-}
-/*==========================================================================*/
-static int convert_UPC(u_char *p)
-{
-	int i;
-	
-	p++;
-	if (fam0L_drive) p[13]=0;
-	for (i=0;i<7;i++)
-	{
-		if (fam1_drive) D_S[d].UPC_buf[i]=swap_nibbles(*p++);
-		else if (fam0L_drive)
-		{
-			D_S[d].UPC_buf[i]=((*p++)<<4)&0xFF;
-			D_S[d].UPC_buf[i] |= *p++;
-		}
-		else if (famT_drive)
-		{
-			return (-1);
-		}
-		else /* CD200 */
-		{
-			return (-1);
-		}
-	}
-	D_S[d].UPC_buf[6] &= 0xF0;
-	return (0);
-}
-/*==========================================================================*/
-static int cc_ReadUPC(void)
-{
-	int i;
-#if TEST_UPC
-	int block, checksum;
-#endif TEST_UPC
-	
-	if (fam2_drive) return (0); /* not implemented yet */
-	if (famT_drive)	return (0); /* not implemented yet */
-#if 1
-	if (fam0_drive) return (0); /* but it should work */
-#endif 1
-	
-	D_S[d].diskstate_flags &= ~upc_bit;
-#if TEST_UPC
-	for (block=CD_MSF_OFFSET+1;block<CD_MSF_OFFSET+200;block++)
-	{
-#endif TEST_UPC
-		clr_cmdbuf();
-		if (fam1_drive)
-		{
-			drvcmd[0]=CMD1_READ_UPC;
-#if TEST_UPC
-			drvcmd[1]=(block>>16)&0xFF;
-			drvcmd[2]=(block>>8)&0xFF;
-			drvcmd[3]=block&0xFF;
-#endif TEST_UPC
-			response_count=8;
-			flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-		}
-		else if (fam0L_drive)
-		{
-			drvcmd[0]=CMD0_READ_UPC;
-#if TEST_UPC
-			drvcmd[2]=(block>>16)&0xFF;
-			drvcmd[3]=(block>>8)&0xFF;
-			drvcmd[4]=block&0xFF;
-#endif TEST_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);
-			}
-		}
-#if TEST_UPC
-		checksum=0;
-#endif TEST_UPC
-		for (i=0;i<(fam1_drive?8:16);i++)
-		{
-#if TEST_UPC
-			checksum |= infobuf[i];
-#endif TEST_UPC
-			sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-		}
-		msgbuf[i*3]=0;
-		msg(DBG_UPC,"UPC info:%s\n", msgbuf);
-#if TEST_UPC
-		if ((checksum&0x7F)!=0) break;
-	}
-#endif TEST_UPC
-	D_S[d].UPC_ctl_adr=0;
-	if (fam1_drive) i=0;
-	else i=2;
-	if ((infobuf[i]&0x80)!=0)
-	{
-		convert_UPC(&infobuf[i]);
-		D_S[d].UPC_ctl_adr = (D_S[d].TocEnt_ctl_adr & 0xF0) | 0x02;
-	}
-	for (i=0;i<7;i++)
-		sprintf(&msgbuf[i*3], " %02X", D_S[d].UPC_buf[i]);
-	sprintf(&msgbuf[i*3], " (%02X)", D_S[d].UPC_ctl_adr);
-	msgbuf[i*3+5]=0;
-	msg(DBG_UPC,"UPC code:%s\n", msgbuf);
-	D_S[d].diskstate_flags |= upc_bit;
-	return (0);
-}
-/*==========================================================================*/
-static int cc_CheckMultiSession(void)
-{
-	int i;
-	
-	if (fam2_drive) return (0);
-	D_S[d].f_multisession=0;
-	D_S[d].lba_multi=0;
-	if (fam0_drive) return (0);
-	clr_cmdbuf();
-	if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_MULTISESS;
-		response_count=6;
-		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
-		i=cmd_out();
-		if (i<0) return (i);
-		if ((infobuf[0]&0x80)!=0)
-		{
-			D_S[d].f_multisession=1;
-			D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
-							make16(infobuf[2],infobuf[3])));
-		}
-	}
-	else if (famL_drive)
-	{
-		drvcmd[0]=CMDL_MULTISESS;
-		drvcmd[1]=3;
-		drvcmd[2]=1;
-		response_count=8;
-		flags_cmd_out=f_putcmd;
-		i=cmd_out();
-		if (i<0) return (i);
-		D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),
-						make16(infobuf[6],infobuf[7])));
-	}
-	else if (famT_drive)
-	{
-		response_count=12;
-		drvcmd[0]=CMDT_DISKINFO;
-		drvcmd[1]=0x02;
-		drvcmd[6]=0;
-		drvcmd[8]=response_count;
-		drvcmd[9]=0x40;
-		i=cmd_out();
-		if (i<0) return (i);
-		D_S[d].first_session=infobuf[2];
-		D_S[d].last_session=infobuf[3];
-		D_S[d].track_of_last_session=infobuf[6];
-		if (D_S[d].first_session!=D_S[d].last_session)
-		{
-			D_S[d].f_multisession=1;
-			D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
-		}
-	}
-	for (i=0;i<response_count;i++)
-		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-	msgbuf[i*3]=0;
-	msg(DBG_MUL,"MultiSession Info:%s (%d)\n", msgbuf, D_S[d].lba_multi);
-	if (D_S[d].lba_multi>200)
-	{
-		D_S[d].f_multisession=1;
-		msg(DBG_MUL,"MultiSession base: %06X\n", D_S[d].lba_multi);
-	}
-	return (0);
-}
-/*==========================================================================*/
-#if FUTURE
-static int cc_SubChanInfo(int frame, int count, u_char *buffer)
-	/* "frame" is a RED BOOK (msf-bin) address */
-{
-	int i;
-	
-	if (fam0L_drive) return (-ENOSYS); /* drive firmware lacks it */
-	if (famT_drive)
-	{
-		return (-1);
-	}
-#if 0
-	if (D_S[d].audio_state!=audio_playing) return (-ENODATA);
-#endif
-	clr_cmdbuf();
-	drvcmd[0]=CMD1_SUBCHANINF;
-	drvcmd[1]=(frame>>16)&0xFF;
-	drvcmd[2]=(frame>>8)&0xFF;
-	drvcmd[3]=frame&0xFF;
-	drvcmd[5]=(count>>8)&0xFF;
-	drvcmd[6]=count&0xFF;
-	flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
-	cmd_type=READ_SC;
-	D_S[d].frame_size=CD_FRAMESIZE_SUB;
-	i=cmd_out(); /* which buffer to use? */
-	return (i);
-}
-#endif FUTURE
-/*==========================================================================*/
-static void check_datarate(void)
-{
-	int i=0;
-	
-	msg(DBG_IOX,"check_datarate entered.\n");
-	datarate=0;
-#if TEST_STI
-	for (i=0;i<=1000;i++) printk(".");
-#endif
-	/* set a timer to make (timed_out_delay!=0) after 1.1 seconds */
-#if 1
-	del_timer(&delay_timer);
-#endif
-	delay_timer.expires=jiffies+11*HZ/10;
-	timed_out_delay=0;
-	add_timer(&delay_timer);
-#if 0
-	msg(DBG_TIM,"delay timer started (11*HZ/10).\n");
-#endif
-	do
-	{
-		i=inb(CDi_status);
-		datarate++;
-#if 1
-		if (datarate>0x6FFFFFFF) break;
-#endif 00000
-	}
-	while (!timed_out_delay);
-	del_timer(&delay_timer);
-#if 0
-	msg(DBG_TIM,"datarate: %04X\n", datarate);
-#endif
-	if (datarate<65536) datarate=65536;
-	maxtim16=datarate*16;
-	maxtim04=datarate*4;
-	maxtim02=datarate*2;
-	maxtim_8=datarate/32;
-#if LONG_TIMING
-	maxtim_data=datarate/100;
-#else
-	maxtim_data=datarate/300;
-#endif LONG_TIMING
-#if 0
-	msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n", maxtim_8, maxtim_data);
-#endif
-}
-/*==========================================================================*/
-#if 0
-static int c2_ReadError(int fam)
-{
-	int i;
-	
-	clr_cmdbuf();
-	response_count=9;
-	clr_respo_buf(9);
-	if (fam==1)
-	{
-		drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
-		i=do_cmd(f_putcmd|f_lopsta|f_getsta|f_ResponseStatus);
-	}
-	else if (fam==2)
-	{
-		drvcmd[0]=CMD2_READ_ERR;
-		i=do_cmd(f_putcmd);
-	}
-	else return (-1);
-	return (i);
-}
-#endif
-/*==========================================================================*/
-static void ask_mail(void)
-{
-	int i;
-	
-	msg(DBG_INF, "please mail the following lines to emoenke@gwdg.de:\n");
-	msg(DBG_INF, "%s\n", VERSION);
-	msg(DBG_INF, "address %03X, type %s, drive %s (ID %d)\n",
-	    CDo_command, type, D_S[d].drive_model, D_S[d].drv_id);
-	for (i=0;i<12;i++)
-		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-	msgbuf[i*3]=0;
-	msg(DBG_INF,"infobuf =%s\n", msgbuf);
-	for (i=0;i<12;i++)
-		sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
-	msgbuf[i*3]=0;
-	msg(DBG_INF,"infobuf =%s\n", msgbuf);
-}
-/*==========================================================================*/
-static int check_version(void)
-{
-	int i, j, l;
-	int teac_possible=0;
-	
-	msg(DBG_INI,"check_version entered.\n");
-	msg(DBG_TE2,"check_version: id=%d, d=%d.\n", D_S[d].drv_id, d);
-	D_S[d].drv_type=0;
-
-	/* check for CR-52x, CR-56x and LCS-7260 */
-	/* clear any pending error state */
-	clr_cmdbuf();
-	drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
-	response_count=9;
-	flags_cmd_out=f_putcmd;
-	i=cmd_out();
-	if (i<0) msg(DBG_INI,"CMD0_READERR returns %d (ok anyway).\n",i);
-	/* read drive version */
-	clr_cmdbuf();
-	for (i=0;i<12;i++) infobuf[i]=0;
-	drvcmd[0]=CMD0_READ_VER; /* same as CMD1_ and CMDL_ */
-	response_count=12; /* fam1: only 11 */
-	flags_cmd_out=f_putcmd;
-	i=cmd_out();
-	if (i<-1) msg(DBG_INI,"CMD0_READ_VER returns %d\n",i);
-	if (i==-11) teac_possible++;
-	j=0;
-	for (i=0;i<12;i++) j+=infobuf[i];
-	if (j)
-	{
-		for (i=0;i<12;i++)
-			sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-		msgbuf[i*3]=0;
-		msg(DBG_IDX,"infobuf =%s\n", msgbuf);
-		msg(DBG_000,"infobuf =%s\n", msgbuf);
-		for (i=0;i<12;i++)
-			sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
-		msgbuf[i*3]=0;
-		msg(DBG_IDX,"infobuf =%s\n", msgbuf);
-		msg(DBG_000,"infobuf =%s\n", msgbuf);
-	}
-	for (i=0;i<4;i++) if (infobuf[i]!=family1[i]) break;
-	if (i==4)
-	{
-		D_S[d].drive_model[0]='C';
-		D_S[d].drive_model[1]='R';
-		D_S[d].drive_model[2]='-';
-		D_S[d].drive_model[3]='5';
-		D_S[d].drive_model[4]=infobuf[i++];
-		D_S[d].drive_model[5]=infobuf[i++];
-		D_S[d].drive_model[6]=0;
-		D_S[d].drv_type=drv_fam1;
-	}
-	if (!D_S[d].drv_type)
-	{
-		for (i=0;i<8;i++) if (infobuf[i]!=family0[i]) break;
-		if (i==8)
-		{
-			D_S[d].drive_model[0]='C';
-			D_S[d].drive_model[1]='R';
-			D_S[d].drive_model[2]='-';
-			D_S[d].drive_model[3]='5';
-			D_S[d].drive_model[4]='2';
-			D_S[d].drive_model[5]='x';
-			D_S[d].drive_model[6]=0;
-			D_S[d].drv_type=drv_fam0;
-		}
-	}
-	if (!D_S[d].drv_type)
-	{
-		for (i=0;i<8;i++) if (infobuf[i]!=familyL[i]) break;
-		if (i==8)
-		{
-			for (j=0;j<8;j++)
-				D_S[d].drive_model[j]=infobuf[j];
-			D_S[d].drive_model[8]=0;
-			D_S[d].drv_type=drv_famL;
-		}
-	}
-	if (!D_S[d].drv_type)
-	{
-		/* check for CD200 */
-		clr_cmdbuf();
-		drvcmd[0]=CMD2_READ_ERR;
-		response_count=9;
-		flags_cmd_out=f_putcmd;
-		i=cmd_out();
-		if (i<0) msg(DBG_INI,"CMD2_READERR returns %d (ok anyway).\n",i);
-		if (i<0) msg(DBG_000,"CMD2_READERR returns %d (ok anyway).\n",i);
-		/* read drive version */
-		clr_cmdbuf();
-		for (i=0;i<12;i++) infobuf[i]=0;
-		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-#if 0
-		OUT(CDo_reset,0);
-		sbp_sleep(6*HZ);
-		OUT(CDo_enable,D_S[d].drv_sel);
-#endif 0
-		drvcmd[0]=CMD2_READ_VER;
-		response_count=12;
-		flags_cmd_out=f_putcmd;
-		i=cmd_out();
-		if (i<0) msg(DBG_INI,"CMD2_READ_VER returns %d\n",i);
-		if (i==-7) teac_possible++;
-		j=0;
-		for (i=0;i<12;i++) j+=infobuf[i];
-		if (j)
-		{
-			for (i=0;i<12;i++)
-				sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
-			msgbuf[i*3]=0;
-			msg(DBG_IDX,"infobuf =%s\n", msgbuf);
-			for (i=0;i<12;i++)
-				sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
-			msgbuf[i*3]=0;
-			msg(DBG_IDX,"infobuf =%s\n", msgbuf);
-		}
-		if (i>=0)
-		{
-			for (i=0;i<5;i++) if (infobuf[i]!=family2[i]) break;
-			if (i==5)
-			{
-				D_S[d].drive_model[0]='C';
-				D_S[d].drive_model[1]='D';
-				D_S[d].drive_model[2]='2';
-				D_S[d].drive_model[3]='0';
-				D_S[d].drive_model[4]='0';
-				D_S[d].drive_model[5]=infobuf[i++];
-				D_S[d].drive_model[6]=infobuf[i++];
-				D_S[d].drive_model[7]=0;
-				D_S[d].drv_type=drv_fam2;
-			}
-		}
-	}
-	if (!D_S[d].drv_type)
-	{
-		/* check for TEAC CD-55A */
-		msg(DBG_TEA,"teac_possible: %d\n",teac_possible);
-		for (j=1;j<=((D_S[d].drv_id==0)?3:1);j++)
-		{
-			for (l=1;l<=((D_S[d].drv_id==0)?10:1);l++)
-			{
-				msg(DBG_TEA,"TEAC reset #%d-%d.\n", j, l);
-				if (sbpro_type==1) OUT(CDo_reset,0);
-				else
-				{
-					OUT(CDo_enable,D_S[d].drv_sel);
-					OUT(CDo_sel_i_d,0);
-					OUT(CDo_command,CMDT_RESET);
-					for (i=0;i<9;i++) OUT(CDo_command,0);
-				}
-				sbp_sleep(5*HZ/10);
-				OUT(CDo_enable,D_S[d].drv_sel);
-				OUT(CDo_sel_i_d,0);
-				i=inb(CDi_status);
-				msg(DBG_TEA,"TEAC CDi_status: %02X.\n",i);
-#if 0
-				if (i&s_not_result_ready) continue; /* drive not present or ready */
-#endif
-				i=inb(CDi_info);
-				msg(DBG_TEA,"TEAC CDi_info: %02X.\n",i);
-				if (i==0x55) break; /* drive found */
-			}
-			if (i==0x55) break; /* drive found */
-		}
-		if (i==0x55) /* drive found */
-		{
-			msg(DBG_TEA,"TEAC drive found.\n");
-			clr_cmdbuf();
-			flags_cmd_out=f_putcmd;
-			response_count=12;
-			drvcmd[0]=CMDT_READ_VER;
-			drvcmd[4]=response_count;
-			for (i=0;i<12;i++) infobuf[i]=0;
-			i=cmd_out_T();
-			if (i!=0) msg(DBG_TEA,"cmd_out_T(CMDT_READ_VER) returns %d.\n",i);
-			for (i=1;i<6;i++) if (infobuf[i]!=familyT[i-1]) break;
-			if (i==6)
-			{
-				D_S[d].drive_model[0]='C';
-				D_S[d].drive_model[1]='D';
-				D_S[d].drive_model[2]='-';
-				D_S[d].drive_model[3]='5';
-				D_S[d].drive_model[4]='5';
-				D_S[d].drive_model[5]=0;
-				D_S[d].drv_type=drv_famT;
-			}
-		}
-	}
-	if (!D_S[d].drv_type)
-	{
-		msg(DBG_TEA,"no drive found at address %03X under ID %d.\n",CDo_command,D_S[d].drv_id);
-		return (-522);
-	}
-	for (j=0;j<4;j++) D_S[d].firmware_version[j]=infobuf[i+j];
-	if (famL_drive)
-	{
-		u_char lcs_firm_e1[]="A E1";
-		u_char lcs_firm_f4[]="A4F4";
-		
-		for (j=0;j<4;j++)
-			if (D_S[d].firmware_version[j]!=lcs_firm_e1[j]) break;
-		if (j==4) D_S[d].drv_type=drv_e1;
-
-		for (j=0;j<4;j++)
-			if (D_S[d].firmware_version[j]!=lcs_firm_f4[j]) break;
-		if (j==4) D_S[d].drv_type=drv_f4;
-
-		if (D_S[d].drv_type==drv_famL) ask_mail();
-	}
-	else if (famT_drive)
-	{
-		j=infobuf[4]; /* one-byte version??? - here: 0x15 */
-		if (j=='5')
-		{
-			D_S[d].firmware_version[0]=infobuf[7];
-			D_S[d].firmware_version[1]=infobuf[8];
-			D_S[d].firmware_version[2]=infobuf[10];
-			D_S[d].firmware_version[3]=infobuf[11];
-		}
-		else
-		{
-			if (j!=0x15) ask_mail();
-			D_S[d].firmware_version[0]='0';
-			D_S[d].firmware_version[1]='.';
-			D_S[d].firmware_version[2]='0'+(j>>4);
-			D_S[d].firmware_version[3]='0'+(j&0x0f);
-		}
-	}
-	else /* CR-52x, CR-56x, CD200 */
-	{
-		j = (D_S[d].firmware_version[0] & 0x0F) * 100 +
-			(D_S[d].firmware_version[2] & 0x0F) *10 +
-				(D_S[d].firmware_version[3] & 0x0F);
-		if (fam0_drive)
-		{
-			if (j<200) D_S[d].drv_type=drv_199;
-			else if (j<201) D_S[d].drv_type=drv_200;
-			else if (j<210) D_S[d].drv_type=drv_201;
-			else if (j<211) D_S[d].drv_type=drv_210;
-			else if (j<300) D_S[d].drv_type=drv_211;
-			else if (j>=300) D_S[d].drv_type=drv_300;
-		}
-		else if (fam1_drive)
-		{
-			if (j<100) D_S[d].drv_type=drv_099;
-			else
-			{
-				D_S[d].drv_type=drv_100;
-				if ((j!=500)&&(j!=102)) ask_mail();
-			}
-		}
-		else if (fam2_drive)
-		{
-			msg(DBG_INF,"new drive CD200 (%s)detected.\n", D_S[d].firmware_version);
-			msg(DBG_INF,"CD200 is not fully supported yet - CD200F should work.\n");
-			if ((j!=1)&&(j!=101)&&(j!=35)) ask_mail(); /* unknown version at time */
-		}
-	}
-	msg(DBG_LCS,"drive type %02X\n",D_S[d].drv_type);
-	msg(DBG_INI,"check_version done.\n");
-	return (0);
-}
-/*==========================================================================*/
-static void switch_drive(int i)
-{
-	d=i;
-	OUT(CDo_enable,D_S[d].drv_sel);
-	msg(DBG_DID,"drive %d (ID=%d) activated.\n", i, D_S[d].drv_id);
-	return;
-}
-/*==========================================================================*/
-#ifdef PATH_CHECK
-/*
- * probe for the presence of an interface card
- */
-static int check_card(int port)
-{
-#undef N_RESPO
-#define N_RESPO 20
-	int i, j, k;
-	u_char response[N_RESPO];
-	u_char save_port0;
-	u_char save_port3;
-	
-	msg(DBG_INI,"check_card entered.\n");
-	save_port0=inb(port+0);
-	save_port3=inb(port+3);
-	
-	for (j=0;j<NR_SBPCD;j++)
-	{
-		OUT(port+3,j) ; /* enable drive #j */
-		OUT(port+0,CMD0_PATH_CHECK);
-		for (i=10;i>0;i--) OUT(port+0,0);
-		for (k=0;k<N_RESPO;k++) response[k]=0;
-		for (k=0;k<N_RESPO;k++)
-		{
-			for (i=10000;i>0;i--)
-			{
-				if (inb(port+1)&s_not_result_ready) continue;
-				response[k]=inb(port+0);
-				break;
-			}
-		}
-		for (i=0;i<N_RESPO;i++)
-			sprintf(&msgbuf[i*3], " %02X", response[i]);
-		msgbuf[i*3]=0;
-		msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
-		OUT(port+0,CMD0_PATH_CHECK);
-		for (i=10;i>0;i--) OUT(port+0,0);
-		for (k=0;k<N_RESPO;k++) response[k]=0xFF;
-		for (k=0;k<N_RESPO;k++)
-		{
-			for (i=10000;i>0;i--)
-			{
-				if (inb(port+1)&s_not_result_ready) continue;
-				response[k]=inb(port+0);
-				break;
-			}
-		}
-		for (i=0;i<N_RESPO;i++)
-			sprintf(&msgbuf[i*3], " %02X", response[i]);
-		msgbuf[i*3]=0;
-		msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
-
-		if (response[0]==0xAA)
-			if (response[1]==0x55)
-				return (0);
-	}
-	for (j=0;j<NR_SBPCD;j++)
-	{
-		OUT(port+3,j) ; /* enable drive #j */
-		OUT(port+0,CMD2_READ_VER);
-		for (i=10;i>0;i--) OUT(port+0,0);
-		for (k=0;k<N_RESPO;k++) response[k]=0;
-		for (k=0;k<N_RESPO;k++)
-		{
-			for (i=1000000;i>0;i--)
-			{
-				if (inb(port+1)&s_not_result_ready) continue;
-				response[k]=inb(port+0);
-				break;
-			}
-		}
-		for (i=0;i<N_RESPO;i++)
-			sprintf(&msgbuf[i*3], " %02X", response[i]);
-		msgbuf[i*3]=0;
-		msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
-
-		OUT(port+0,CMD2_READ_VER);
-		for (i=10;i>0;i--) OUT(port+0,0);
-		for (k=0;k<N_RESPO;k++) response[k]=0xFF;
-		for (k=0;k<N_RESPO;k++)
-		{
-			for (i=1000000;i>0;i--)
-			{
-				if (inb(port+1)&s_not_result_ready) continue;
-				response[k]=inb(port+0);
-				break;
-			}
-		}
-		for (i=0;i<N_RESPO;i++)
-			sprintf(&msgbuf[i*3], " %02X", response[i]);
-		msgbuf[i*3]=0;
-		msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
-
-		if (response[0]==0xAA)
-			if (response[1]==0x55)
-				return (0);
-	}
-	OUT(port+0,save_port0);
-	OUT(port+3,save_port3);
-	return (0); /* in any case - no real "function" at time */
-}
-#endif PATH_CHECK
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * probe for the presence of drives on the selected controller
- */
-static int check_drives(void)
-{
-	int i, j;
-	
-	msg(DBG_INI,"check_drives entered.\n");
-	ndrives=0;
-	for (j=0;j<MAX_DRIVES;j++)
-	{
-		D_S[ndrives].drv_id=j;
-		if (sbpro_type==1) D_S[ndrives].drv_sel=(j&0x01)<<1|(j&0x02)>>1;
-		else D_S[ndrives].drv_sel=j;
-		switch_drive(ndrives);
-		msg(DBG_INI,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
-		msg(DBG_000,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
-		i=check_version();
-		if (i<0) msg(DBG_INI,"check_version returns %d.\n",i);
-		else
-		{
-			D_S[d].drv_options=drv_pattern[j];
-			if (fam0L_drive) D_S[d].drv_options&=~(speed_auto|speed_300|speed_150);
-			msg(DBG_INF, "Drive %d (ID=%d): %.9s (%.4s) at 0x%03X (type %d)\n",
-			    d,
-			    D_S[d].drv_id,
-			    D_S[d].drive_model,
-			    D_S[d].firmware_version,
-			    CDo_command,
-			    sbpro_type);
-			ndrives++;
-		}
-	}
-	for (j=ndrives;j<NR_SBPCD;j++) D_S[j].drv_id=-1;
-	if (ndrives==0) return (-1);
-	return (0);
-}
-/*==========================================================================*/
-#if FUTURE
-/*
- *  obtain if requested service disturbs current audio state
- */            
-static int obey_audio_state(u_char audio_state, u_char func,u_char subfunc)
-{
-	switch (audio_state)                   /* audio status from controller  */
-	{
-	case aud_11: /* "audio play in progress" */
-	case audx11:
-		switch (func)                      /* DOS command code */
-		{
-		case cmd_07: /* input flush  */
-		case cmd_0d: /* open device  */
-		case cmd_0e: /* close device */
-		case cmd_0c: /* ioctl output */
-			return (1);
-		case cmd_03: /* ioctl input  */
-			switch (subfunc)
-				/* DOS ioctl input subfunction */
-			{
-			case cxi_00:
-			case cxi_06:
-			case cxi_09:
-				return (1);
-			default:
-				return (ERROR15);
-			}
-			return (1);
-		default:
-			return (ERROR15);
-		}
-		return (1);
-	case aud_12:                  /* "audio play paused"      */
-	case audx12:
-		return (1);
-	default:
-		return (2);
-	}
-}
-/*==========================================================================*/
-/* allowed is only
- * ioctl_o, flush_input, open_device, close_device, 
- * tell_address, tell_volume, tell_capabiliti,
- * tell_framesize, tell_CD_changed, tell_audio_posi
- */
-static int check_allowed1(u_char func1, u_char func2)
-{
-#if 000
-	if (func1==ioctl_o) return (0);
-	if (func1==read_long) return (-1);
-	if (func1==read_long_prefetch) return (-1);
-	if (func1==seek) return (-1);
-	if (func1==audio_play) return (-1);
-	if (func1==audio_pause) return (-1);
-	if (func1==audio_resume) return (-1);
-	if (func1!=ioctl_i) return (0);
-	if (func2==tell_SubQ_run_tot) return (-1);
-	if (func2==tell_cdsize) return (-1);
-	if (func2==tell_TocDescrip) return (-1);
-	if (func2==tell_TocEntry) return (-1);
-	if (func2==tell_subQ_info) return (-1);
-	if (fam1_drive) if (func2==tell_SubChanInfo) return (-1);
-	if (func2==tell_UPC) return (-1);
-#else
-	return (0);
-#endif 000
-}
-/*==========================================================================*/
-static int check_allowed2(u_char func1, u_char func2)
-{
-#if 000
-	if (func1==read_long) return (-1);
-	if (func1==read_long_prefetch) return (-1);
-	if (func1==seek) return (-1);
-	if (func1==audio_play) return (-1);
-  if (func1!=ioctl_o) return (0);
-	if (fam1_drive)
-	{
-		if (func2==EjectDisk) return (-1);
-		if (func2==CloseTray) return (-1);
-	}
-#else
-	return (0);
-#endif 000
-}
-/*==========================================================================*/
-static int check_allowed3(u_char func1, u_char func2)
-{
-#if 000
-	if (func1==ioctl_i)
-	{
-		if (func2==tell_address) return (0);
-		if (func2==tell_capabiliti) return (0);
-		if (func2==tell_CD_changed) return (0);
-		if (fam0L_drive) if (func2==tell_SubChanInfo) return (0);
-		return (-1);
-	}
-	if (func1==ioctl_o)
-	{
-		if (func2==DriveReset) return (0);
-		if (fam0L_drive)
-		{
-			if (func2==EjectDisk) return (0);
-			if (func2==LockDoor) return (0);
-	  if (func2==CloseTray) return (0);
-		}
-		return (-1);
-    }
-	if (func1==flush_input) return (-1);
-	if (func1==read_long) return (-1);
-	if (func1==read_long_prefetch) return (-1);
-	if (func1==seek) return (-1);
-	if (func1==audio_play) return (-1);
-	if (func1==audio_pause) return (-1);
-	if (func1==audio_resume) return (-1);
-#else
-	return (0);
-#endif 000
-}
-/*==========================================================================*/
-static int seek_pos_audio_end(void)
-{
-	int i;
-
-	i=msf2blk(D_S[d].pos_audio_end)-1;
-	if (i<0) return (-1);
-	i=cc_Seek(i,0);
-	return (i);
-}
-#endif FUTURE
-/*==========================================================================*/
-static int ReadToC(void)
-{
-	int i, j;
-	D_S[d].diskstate_flags &= ~toc_bit;
-	D_S[d].ored_ctl_adr=0;
-	for (j=D_S[d].n_first_track;j<=D_S[d].n_last_track;j++)
-	{
-		i=cc_ReadTocEntry(j);
-		if (i<0)
-		{
-			msg(DBG_INF,"cc_ReadTocEntry(%d) returns %d.\n",j,i);
-			return (i);
-		}
-		D_S[d].TocBuffer[j].nixbyte=D_S[d].TocEnt_nixbyte;
-		D_S[d].TocBuffer[j].ctl_adr=D_S[d].TocEnt_ctl_adr;
-		D_S[d].TocBuffer[j].number=D_S[d].TocEnt_number;
-		D_S[d].TocBuffer[j].format=D_S[d].TocEnt_format;
-		D_S[d].TocBuffer[j].address=D_S[d].TocEnt_address;
-		D_S[d].ored_ctl_adr |= D_S[d].TocEnt_ctl_adr;
-	}
-	/* fake entry for LeadOut Track */
-	D_S[d].TocBuffer[j].nixbyte=0;
-	D_S[d].TocBuffer[j].ctl_adr=0;
-	D_S[d].TocBuffer[j].number=CDROM_LEADOUT;
-	D_S[d].TocBuffer[j].format=0;
-	D_S[d].TocBuffer[j].address=D_S[d].size_msf;
-	
-	D_S[d].diskstate_flags |= toc_bit;
-	return (0);
-}
-/*==========================================================================*/
-static int DiskInfo(void)
-{
-	int i, j;
-	
-	D_S[d].mode=READ_M1;
-	
-#undef LOOP_COUNT
-#define LOOP_COUNT 10 /* needed for some "old" drives */
-	
-	msg(DBG_000,"DiskInfo entered.\n");
-	for (j=1;j<LOOP_COUNT;j++)
-	{
-		i=SetSpeed();
-		if (i<0)
-		{
-			msg(DBG_INF,"DiskInfo: SetSpeed returns %d\n", i);
-			continue;
-		}
-		i=cc_ModeSense();
-		if (i<0)
-		{
-			msg(DBG_INF,"DiskInfo: cc_ModeSense returns %d\n", i);
-			continue;
-		}
-		i=cc_ReadCapacity();
-		if (i>=0) break;
-		msg(DBG_INF,"DiskInfo: ReadCapacity #%d returns %d\n", j, i);
-		i=cc_DriveReset();
-	}
-	if (j==LOOP_COUNT) return (-33); /* give up */
-	
-	i=cc_ReadTocDescr();
-	if (i<0)
-	{
-		msg(DBG_INF,"DiskInfo: ReadTocDescr returns %d\n", i);
-		return (i);
-	}
-	i=ReadToC();
-	if (i<0)
-	{
-		msg(DBG_INF,"DiskInfo: ReadToC returns %d\n", i);
-		return (i);
-	}
-	i=cc_CheckMultiSession();
-	if (i<0)
-	{
-		msg(DBG_INF,"DiskInfo: cc_CheckMultiSession returns %d\n", i);
-		return (i);
-	}
-	if (D_S[d].f_multisession) D_S[d].sbp_bufsiz=1;  /* possibly a weird PhotoCD */
-	else D_S[d].sbp_bufsiz=SBP_BUFFER_FRAMES;
-	i=cc_ReadTocEntry(D_S[d].n_first_track);
-	if (i<0)
-	{
-		msg(DBG_INF,"DiskInfo: cc_ReadTocEntry(1) returns %d\n", i);
-		return (i);
-	}
-	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))
-	{
-		/* XA disk with old drive */
-		cc_ModeSelect(CD_FRAMESIZE_RAW1);
-		cc_ModeSense();
-	}
-	if (famT_drive)	cc_prep_mode_T();
-	msg(DBG_000,"DiskInfo done.\n");
-	return (0);
-}
-/*==========================================================================*/
-#if FUTURE
-/*
- *  called always if driver gets entered
- *  returns 0 or ERROR2 or ERROR15
- */
-static int prepare(u_char func, u_char subfunc)
-{
-	int i;
-	
-	if (fam0L_drive)
-	{
-		i=inb(CDi_status);
-		if (i&s_attention) GetStatus();
-	}
-	else if (fam1_drive) GetStatus();
-	else if (fam2_drive) GetStatus();
-	else if (famT_drive) GetStatus();
-	if (D_S[d].CD_changed==0xFF)
-	{
-		D_S[d].diskstate_flags=0;
-		D_S[d].audio_state=0;
-		if (!st_diskok)
-		{
-			i=check_allowed1(func,subfunc);
-			if (i<0) return (-2);
-		}
-		else 
-		{
-			i=check_allowed3(func,subfunc);
-			if (i<0)
-			{
-				D_S[d].CD_changed=1;
-				return (-15);
-			}
-		}
-	}
-	else
-	{
-		if (!st_diskok)
-		{
-			D_S[d].diskstate_flags=0;
-			D_S[d].audio_state=0;
-			i=check_allowed1(func,subfunc);
-			if (i<0) return (-2);
-		}
-		else
-		{ 
-			if (st_busy)
-			{
-				if (D_S[d].audio_state!=audio_pausing)
-				{
-					i=check_allowed2(func,subfunc);
-					if (i<0) return (-2);
-				}
-			}
-			else
-			{
-				if (D_S[d].audio_state==audio_playing) seek_pos_audio_end();
-				D_S[d].audio_state=0;
-			}
-			if (!frame_size_valid)
-			{
-				i=DiskInfo();
-				if (i<0)
-				{
-					D_S[d].diskstate_flags=0;
-					D_S[d].audio_state=0;
-					i=check_allowed1(func,subfunc);
-					if (i<0) return (-2);
-				}
-			}
-		}
-    }
-	return (0);
-}
-#endif FUTURE
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * Check the results of the "get status" command.
- */
-static int sbp_status(void)
-{
-	int st;
-	
-	st=ResponseStatus();
-	if (st<0)
-	{
-		msg(DBG_INF,"sbp_status: timeout.\n");
-		return (0);
-	}
-	
-	if (!st_spinning) msg(DBG_SPI,"motor got off - ignoring.\n");
-	
-	if (st_check) 
-	{
-		msg(DBG_INF,"st_check detected - retrying.\n");
-		return (0);
-	}
-	if (!st_door_closed)
-	{
-		msg(DBG_INF,"door is open - retrying.\n");
-		return (0);
-	}
-	if (!st_caddy_in)
-	{
-		msg(DBG_INF,"disk removed - retrying.\n");
-		return (0);
-	}
-	if (!st_diskok) 
-	{
-		msg(DBG_INF,"!st_diskok detected - retrying.\n");
-		return (0);
-	}
-	if (st_busy) 
-	{
-		msg(DBG_INF,"st_busy detected - retrying.\n");
-		return (0);
-	}
-	return (1);
-}
-/*==========================================================================*/
-
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * ioctl support
- */
-static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		       u_long arg)
-{
-	int i, st;
-	
-	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);
-	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
-	{
-		msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev);
-		return (-ENXIO);             /* no such drive */
-	}
-	if (d!=i) switch_drive(i);
-	
-#if 0
-	st=GetStatus();
-	if (st<0) return (-EIO);
-	
-	if (!toc_valid)
-	{
-		i=DiskInfo();
-		if (i<0) return (-EIO);	/* error reading TOC */
-	}
-#endif
-	
-	msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd);
-	switch (cmd) 		/* Sun-compatible */
-	{
-	case DDIOCSDBG:		/* DDI Debug */
-		if (!suser()) return (-EPERM);
-		i=sbpcd_dbg_ioctl(arg,1);
-		return (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 (-EIO);
-			if (famL_drive) i=cc_Pause_Resume(1);
-			else i=cc_ReadSubQ();
-			if (i<0) return (-EIO);
-			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
-			D_S[d].audio_state=audio_pausing;
-			return (0);
-		case audio_pausing:
-			i=cc_Seek(D_S[d].pos_audio_start,1);
-			if (i<0) return (-EIO);
-			return (0);
-		default:
-			return (-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 (-EIO);
-		D_S[d].audio_state=audio_playing;
-		return (0);
-		
-	case CDROMPLAYMSF:
-		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
-		if (D_S[d].audio_state==audio_playing)
-		{
-			i=cc_Pause_Resume(1);
-			if (i<0) return (-EIO);
-			i=cc_ReadSubQ();
-			if (i<0) return (-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 (st);
-		memcpy_fromfs(&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);
-		msg(DBG_IOC,"ioctl: cc_PlayAudio returns %d\n",i);
-#if 0
-		if (i<0) return (-EIO);
-#endif 0
-		D_S[d].audio_state=audio_playing;
-		return (0);
-		
-	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
-		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
-		if (D_S[d].audio_state==audio_playing)
-		{
-			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
-			return (0);
-			return (-EINVAL);
-		}
-		st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
-		if (st<0)
-		{
-			msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n");
-			return (st);
-		}
-		memcpy_fromfs(&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 (-EINVAL);
-		if (ti.cdti_trk0>D_S[d].n_last_track) return (-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 0
-		if (i<0) return (-EIO);
-#endif 0
-		D_S[d].audio_state=audio_playing;
-		return (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 (st);
-		memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
-		return (0);
-		
-	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
-		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
-		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_tocentry));
-		if (st) return (st);
-		memcpy_fromfs(&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 (-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 (-EINVAL);
-		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
-		if (st) return (st);
-		memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
-		return (0);
-		
-	case CDROMRESET:      /* hard reset the drive */
-		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
-		i=DriveReset();
-		D_S[d].audio_state=0;
-		return (i);
-		
-	case CDROMSTOP:      /* Spin down the drive */
-		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
-		i=cc_Pause_Resume(1);
-		D_S[d].audio_state=0;
-		return (i);
-		
-	case CDROMSTART:  /* Spin up the drive */
-		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
-		cc_SpinUp();
-		D_S[d].audio_state=0;
-		return (0);
-		
-	case CDROMEJECT:
-		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
-		if (fam0_drive) return (0);
-		if (D_S[d].open_count>1) return (-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 (-EIO);
-		D_S[d].CD_changed=0xFF;
-		D_S[d].diskstate_flags=0;
-		D_S[d].audio_state=0;
-		return (0);
-		
-	case CDROMEJECT_SW:
-		msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
-		if (fam0_drive) return (0);
-		D_S[d].f_eject=arg;
-		return (0);
-		
-	case CDROMVOLCTRL:   /* Volume control */
-		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
-		st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
-		if (st) return (st);
-		memcpy_fromfs(&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 (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 (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;
-		memcpy_tofs((void *)arg,&volctrl,sizeof(volctrl));
-		return (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 (-EIO);
-					    }
-		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st)	return (st);
-		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st) return (st);
-		memcpy_fromfs(&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_tofs((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 (0);
-		
-	case CDROMREADMODE1:
-		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
-		cc_ModeSelect(CD_FRAMESIZE);
-		cc_ModeSense();
-		D_S[d].mode=READ_M1;
-		return (0);
-		
-	case CDROMREADMODE2: /* not usable at the moment */
-		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
-		cc_ModeSelect(CD_FRAMESIZE_RAW1);
-		cc_ModeSense();
-		D_S[d].mode=READ_M2;
-		return (0);
-		
-	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
-		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
-#ifdef MODULE
-		if (D_S[d].sbp_audsiz>0)
-			vfree(D_S[d].aud_buf);
-#endif MODULE
-		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);
-		}
-		return (D_S[d].sbp_audsiz);
-
-	case CDROMREADAUDIO:
-	{ /* start of CDROMREADAUDIO */
-		int i=0, j=0, frame, block;
-		u_int try=0;
-		u_long timeout;
-		u_char *p;
-		u_int data_tries = 0;
-		u_int data_waits = 0;
-		u_int data_retrying = 0;
-		int status_tries;
-		int error_flag;
-		
-		msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
-		if (fam0_drive) return (-EINVAL);
-		if (famL_drive) return (-EINVAL);
-		if (fam2_drive) return (-EINVAL);
-		if (famT_drive) return (-EINVAL);
-		if (D_S[d].aud_buf==NULL) return (-EINVAL);
-		i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
-		if (i) return (i);
-		memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
-		if (read_audio.nframes>D_S[d].sbp_audsiz) return (-EINVAL);
-		i=verify_area(VERIFY_WRITE, read_audio.buf,
-			      read_audio.nframes*CD_FRAMESIZE_RAW);
-		if (i) return (i);
-		
-		if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
-			block=msf2lba(&read_audio.addr.msf.minute);
-		else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
-			block=read_audio.addr.lba;
-		else return (-EINVAL);
-		i=cc_SetSpeed(speed_150,0,0);
-		if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
-		msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
-		    block, blk2msf(block));
-		msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
-		while (busy_data) sbp_sleep(HZ/10); /* wait a bit */
-		busy_audio=1;
-		error_flag=0;
-		for (data_tries=5; data_tries>0; data_tries--)
-		{
-			msg(DBG_AUD,"data_tries=%d ...\n", data_tries);
-			D_S[d].mode=READ_AU;
-			cc_ModeSelect(CD_FRAMESIZE_RAW);
-			cc_ModeSense();
-			for (status_tries=3; status_tries > 0; status_tries--)
-			{
-				flags_cmd_out |= f_respo3;
-				cc_ReadStatus();
-				if (sbp_status() != 0) break;
-				sbp_sleep(1);    /* wait a bit, try again */
-			}
-			if (status_tries == 0)
-			{
-				msg(DBG_AUD,"read_audio: sbp_status: failed after 3 tries.\n");
-				continue;
-			}
-			msg(DBG_AUD,"read_audio: sbp_status: ok.\n");
-			
-			flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
-			if (fam0L_drive)
-			{
-				flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
-				cmd_type=READ_M2;
-				drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
-				drvcmd[1]=(block>>16)&0x000000ff;
-				drvcmd[2]=(block>>8)&0x000000ff;
-				drvcmd[3]=block&0x000000ff;
-				drvcmd[4]=0;
-				drvcmd[5]=read_audio.nframes; /* # of frames */
-				drvcmd[6]=0;
-			}
-			else if (fam1_drive)
-			{
-				drvcmd[0]=CMD1_READ; /* "read frames", new drives */
-				lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-				drvcmd[4]=0;
-				drvcmd[5]=0;
-				drvcmd[6]=read_audio.nframes; /* # of frames */
-			}
-			else if (fam2_drive) /* CD200: not tested yet */
-			{
-			}
-			else if (famT_drive) /* CD-55A: not tested yet */
-			{
-			}
-			msg(DBG_AUD,"read_audio: before giving \"read\" command.\n");
-			for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
-			sbp_sleep(0);
-			msg(DBG_AUD,"read_audio: after giving \"read\" command.\n");
-			for (frame=1;frame<2 && !error_flag; frame++)
-			{
-				try=maxtim_data;
-				for (timeout=jiffies+9*HZ; ; )
-				{
-					for ( ; try!=0;try--)
-					{
-						j=inb(CDi_status);
-						if (!(j&s_not_data_ready)) break;
-						if (!(j&s_not_result_ready)) break;
-						if (fam0L_drive) if (j&s_attention) break;
-					}
-					if (try != 0 || timeout <= jiffies) break;
-					if (data_retrying == 0) data_waits++;
-					data_retrying = 1;
-					sbp_sleep(1);
-					try = 1;
-				}
-				if (try==0)
-				{
-					msg(DBG_INF,"read_audio: sbp_data: CDi_status timeout.\n");
-					error_flag++;
-					break;
-				}
-				msg(DBG_AUD,"read_audio: sbp_data: CDi_status ok.\n");
-				if (j&s_not_data_ready)
-				{
-					msg(DBG_INF, "read_audio: sbp_data: DATA_READY timeout.\n");
-					error_flag++;
-					break;
-				}
-				msg(DBG_AUD,"read_audio: before reading data.\n");
-				error_flag=0;
-				p = D_S[d].aud_buf;
-				if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-				insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
-				if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-				data_retrying = 0;
-			}
-			msg(DBG_AUD,"read_audio: after reading data.\n");
-			if (error_flag)    /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
-			{
-				msg(DBG_AUD,"read_audio: read aborted by drive\n");
-#if 0000
-				i=cc_DriveReset();                /* ugly fix to prevent a hang */
-#else
-				i=cc_ReadError();
-#endif 0000
-				continue;
-			}
-			if (fam0L_drive)
-			{
-				i=maxtim_data;
-				for (timeout=jiffies+9*HZ; timeout > jiffies; timeout--)
-				{
-					for ( ;i!=0;i--)
-					{
-						j=inb(CDi_status);
-						if (!(j&s_not_data_ready)) break;
-						if (!(j&s_not_result_ready)) break;
-						if (j&s_attention) break;
-					}
-					if (i != 0 || timeout <= jiffies) break;
-					sbp_sleep(0);
-					i = 1;
-				}
-				if (i==0) msg(DBG_AUD,"read_audio: STATUS TIMEOUT AFTER READ");
-				if (!(j&s_attention))
-				{
-					msg(DBG_AUD,"read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n");
-					i=cc_DriveReset();  /* ugly fix to prevent a hang */
-					continue;
-				}
-			}
-			do
-			{
-				if (fam0L_drive) cc_ReadStatus();
-				i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
-				if (i<0) { msg(DBG_AUD,
-					       "read_audio: cc_ReadStatus error after read: %02X\n",
-					       D_S[d].status_bits);
-					   continue; /* FIXME */
-				   }
-			}
-			while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
-			if (st_check)
-			{
-				i=cc_ReadError();
-				msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i);
-				continue;
-			}
-			memcpy_tofs((u_char *) read_audio.buf,
-				    (u_char *) D_S[d].aud_buf,
-				    read_audio.nframes*CD_FRAMESIZE_RAW);
-			msg(DBG_AUD,"read_audio: memcpy_tofs done.\n");
-			break;
-		}
-		cc_ModeSelect(CD_FRAMESIZE);
-		cc_ModeSense();
-		D_S[d].mode=READ_M1;
-		busy_audio=0;
-		if (data_tries == 0)
-		{
-			msg(DBG_AUD,"read_audio: failed after 5 tries.\n");
-			return (-8);
-		}
-		msg(DBG_AUD,"read_audio: successful return.\n");
-		return (0);
-	} /* end of CDROMREADAUDIO */
-		
-	case CDROMMULTISESSION: /* tell start-of-last-session */
-		msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n");
-		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_multisession));
-		if (st) return (st);
-		memcpy_fromfs(&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 (-EINVAL);
-		if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
-		else ms_info.xa_flag=0; /* invalid redirection address */
-		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
-		if (st) return (st);
-		memcpy_tofs((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 (0);
-		
-	case BLKRASET:
-		if(!suser())  return -EACCES;
-		if(!(inode->i_rdev)) return -EINVAL;
-		if(arg > 0xff) return -EINVAL;
-		read_ahead[MAJOR(inode->i_rdev)] = arg;
-		return (0);
-		
-	default:
-		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
-		return (-EINVAL);
-	} /* end switch(cmd) */
-}
-/*==========================================================================*/
-/*
- *  Take care of the different block sizes between cdrom and Linux.
- */
-static void sbp_transfer(void)
-{
-	long offs;
-	
-	while ( (CURRENT->nr_sectors > 0) &&
-	       (CURRENT->sector/4 >= D_S[d].sbp_first_frame) &&
-	       (CURRENT->sector/4 <= D_S[d].sbp_last_frame) )
-	{
-		offs = (CURRENT->sector - D_S[d].sbp_first_frame * 4) * 512;
-		memcpy(CURRENT->buffer, D_S[d].sbp_buf + offs, 512);
-		CURRENT->nr_sectors--;
-		CURRENT->sector++;
-		CURRENT->buffer += 512;
-	}
-}
-/*==========================================================================*/
-/*
- *  I/O request routine, called from Linux kernel.
- */
-static void DO_SBPCD_REQUEST(void)
-{
-	u_int block;
-	u_int nsect;
-	int i, status_tries, data_tries;
-	
- request_loop:
-	INIT_REQUEST;
-	sti();
-	
-	if ((CURRENT == NULL) || CURRENT->rq_status == RQ_INACTIVE)
-		goto err_done;
-	if (CURRENT -> sector == -1)
-		goto err_done;
-	if (CURRENT->cmd != READ)
-	{
-		msg(DBG_INF, "bad cmd %d\n", CURRENT->cmd);
-		goto err_done;
-	}
-	i = MINOR(CURRENT->rq_dev);
-	if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
-	{
-		msg(DBG_INF, "do_request: bad device: %s\n",
-			kdevname(CURRENT->rq_dev));
-		goto err_done;
-	}
-	while (busy_audio) sbp_sleep(HZ); /* wait a bit */
-	busy_data=1;
-	
-	if (D_S[i].audio_state==audio_playing) goto err_done;
-	if (d!=i) switch_drive(i);
-	
-	block = CURRENT->sector; /* always numbered as 512-byte-pieces */
-	nsect = CURRENT->nr_sectors; /* always counted as 512-byte-pieces */
-	
-	msg(DBG_BSZ,"read sector %d (%d sectors)\n", block, nsect);
-#if 0
-	msg(DBG_MUL,"read LBA %d\n", block/4);
-#endif
-	
-	sbp_transfer();
-	/* if we satisfied the request from the buffer, we're done. */
-	if (CURRENT->nr_sectors == 0)
-	{
-		end_request(1);
-		goto request_loop;
-	}
-
-#if FUTURE
-	i=prepare(0,0); /* at moment not really a hassle check, but ... */
-	if (i!=0)
-		msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i);
-#endif FUTURE
-	
-	if (!st_spinning) cc_SpinUp();
-	
-	for (data_tries=n_retries; data_tries > 0; data_tries--)
-	{
-		for (status_tries=3; status_tries > 0; status_tries--)
-		{
-			flags_cmd_out |= f_respo3;
-			cc_ReadStatus();
-			if (sbp_status() != 0) break;
-			if (st_check) cc_ReadError();
-			sbp_sleep(1);    /* wait a bit, try again */
-		}
-		if (status_tries == 0)
-		{
-			msg(DBG_INF,"sbp_status: failed after 3 tries\n");
-			break;
-		}
-		
-		sbp_read_cmd();
-		sbp_sleep(0);
-		if (sbp_data() != 0)
-		{
-			end_request(1);
-			goto request_loop;
-		}
-	}
-	
- err_done:
-	busy_data=0;
-	end_request(0);
-	sbp_sleep(0);    /* wait a bit, try again */
-	goto request_loop;
-}
-/*==========================================================================*/
-/*
- *  build and send the READ command.
- */
-static void sbp_read_cmd(void)
-{
-#undef OLD
-
-	int i;
-	int block;
-	
-	D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;      /* purge buffer */
-	D_S[d].sbp_current = 0;
-	block=CURRENT->sector/4;
-	if (block+D_S[d].sbp_bufsiz <= D_S[d].CDsize_frm)
-		D_S[d].sbp_read_frames = D_S[d].sbp_bufsiz;
-	else
-	{
-		D_S[d].sbp_read_frames=D_S[d].CDsize_frm-block;
-		/* avoid reading past end of data */
-		if (D_S[d].sbp_read_frames < 1)
-		{
-			msg(DBG_INF,"requested frame %d, CD size %d ???\n",
-			    block, D_S[d].CDsize_frm);
-			D_S[d].sbp_read_frames=1;
-		}
-	}
-	
-	flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
-	clr_cmdbuf();
-	if (fam0L_drive)
-	{
-		flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
-		if (D_S[d].xa_byte==0x20)
-		{
-			cmd_type=READ_M2;
-			drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
-			drvcmd[1]=(block>>16)&0x000000ff;
-			drvcmd[2]=(block>>8)&0x000000ff;
-			drvcmd[3]=block&0x000000ff;
-			drvcmd[5]=D_S[d].sbp_read_frames;
-		}
-		else
-		{
-			drvcmd[0]=CMD0_READ; /* "read frames", old drives */
-			if (D_S[d].drv_type>=drv_201)
-			{
-				lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
-				bin2bcdx(&drvcmd[1]);
-				bin2bcdx(&drvcmd[2]);
-				bin2bcdx(&drvcmd[3]);
-			}
-			else
-			{
-				drvcmd[1]=(block>>16)&0x000000ff;
-				drvcmd[2]=(block>>8)&0x000000ff;
-				drvcmd[3]=block&0x000000ff;
-			}
-			drvcmd[5]=D_S[d].sbp_read_frames;
-			drvcmd[6]=(D_S[d].drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
-		}
-	}
-	else if (fam1_drive)
-	{
-		drvcmd[0]=CMD1_READ;
-		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-		drvcmd[6]=D_S[d].sbp_read_frames;
-	}
-	else if (fam2_drive)
-	{
-		drvcmd[0]=CMD2_READ;
-		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
-		drvcmd[5]=D_S[d].sbp_read_frames;
-		drvcmd[6]=0x02;
-	}
-	else if (famT_drive)
-	{
-		drvcmd[0]=CMDT_READ;
-		drvcmd[2]=(block>>24)&0x0ff;
-		drvcmd[3]=(block>>16)&0x0ff;
-		drvcmd[4]=(block>>8)&0x0ff;
-		drvcmd[5]=block&0x0ff;
-		drvcmd[7]=(D_S[d].sbp_read_frames>>8)&0x0ff;
-		drvcmd[8]=D_S[d].sbp_read_frames&0x0ff;
-	}
-#ifdef OLD
-	SBPCD_CLI;
-	for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
-	if (famT_drive) for (i=7;i<10;i++) OUT(CDo_command,drvcmd[i]);
-	SBPCD_STI;
-#else
-	flags_cmd_out=f_putcmd;
-	response_count=0;
-	i=cmd_out(); /* immediate return here - read data "ourselves" */
-	if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i);
-#endif OLD
-	return;
-}
-/*==========================================================================*/
-/*
- *  Check the completion of the read-data command.  On success, read
- *  the D_S[d].sbp_bufsiz * 2048 bytes of data from the disk into buffer.
- */
-static int sbp_data(void)
-{
-	int i=0, j=0, l, frame;
-	u_int try=0;
-	u_long timeout;
-	u_char *p;
-	u_int data_tries = 0;
-	u_int data_waits = 0;
-	u_int data_retrying = 0;
-	int error_flag;
-	int xa_count;
-	int max_latency;
-	int success;
-	int wait;
-	int duration;
-	
-	error_flag=0;
-	success=0;
-#if LONG_TIMING
-	max_latency=9*HZ;
-#else
-	if (D_S[d].f_multisession) max_latency=9*HZ;
-	else max_latency=3*HZ;
-#endif
-	msg(DBG_TE2,"beginning to READ\n");
-	duration=jiffies;
-	for (frame=0;frame<D_S[d].sbp_read_frames&&!error_flag; frame++)
-	{
-		SBPCD_CLI;
-		
-		del_timer(&data_timer);
-		data_timer.expires=jiffies+max_latency;
-		timed_out_data=0;
-		add_timer(&data_timer);
-		while (!timed_out_data) 
-		{
-			if (D_S[d].f_multisession) try=maxtim_data*4;
-			else try=maxtim_data;
-			msg(DBG_000,"sbp_data: CDi_status loop: try=%d.\n",try);
-			for ( ; try!=0;try--)
-			{
-				j=inb(CDi_status);
-				if (!(j&s_not_data_ready)) break;;
-				if (!(j&s_not_result_ready)) break;
-				if (fam0L_drive) if (j&s_attention) break;
-			}
-			if (!(j&s_not_data_ready)) goto data_ready;
-			if (try==0)
-			{
-				if (data_retrying == 0) data_waits++;
-				data_retrying = 1;
-				msg(DBG_000,"sbp_data: CDi_status loop: sleeping.\n");
-				sbp_sleep(1);
-				try = 1;
-			}
-		}
-		msg(DBG_INF,"sbp_data: CDi_status loop expired.\n");
-	data_ready:
-		del_timer(&data_timer);
-
-		if (timed_out_data)
-		{
-			msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j);
-			error_flag++;
-			break;
-		}
-		if (try==0)
-		{
-			msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j);
-			error_flag++;
-			break;
-		}
-		if (!(j&s_not_result_ready))
-		{
-			msg(DBG_INF, "sbp_data: RESULT_READY where DATA_READY awaited (%02X).\n", j);
-			response_count=20;
-			j=ResponseInfo();
-			j=inb(CDi_status);
-		}
-		if (j&s_not_data_ready)
-		{
-			if ((D_S[d].ored_ctl_adr&0x40)==0)
-				msg(DBG_INF, "CD contains no data tracks.\n");
-			else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j);
-			error_flag++;
-			break;
-		}
-		SBPCD_STI;
-		error_flag=0;
-		msg(DBG_000, "sbp_data: beginning to read.\n");
-		p = D_S[d].sbp_buf + frame *  CD_FRAMESIZE;
-		if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-		if (cmd_type==READ_M2) insb(CDi_data, xa_head_buf, CD_XA_HEAD);
-		insb(CDi_data, p, CD_FRAMESIZE);
-		if (cmd_type==READ_M2) insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
-		if (famT_drive)	msg(DBG_TE2, "================frame read=================.\n");
-		D_S[d].sbp_current++;
-		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-		if (cmd_type==READ_M2)
-		{
-			for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++)
-				sprintf(&msgbuf[xa_count*3], " %02X", xa_head_buf[xa_count]);
-			msgbuf[xa_count*3]=0;
-			msg(DBG_XA1,"xa head:%s\n", msgbuf);
-		}
-		data_retrying = 0;
-		data_tries++;
-		if (data_tries >= 1000)
-		{
-			msg(DBG_INF,"sbp_data() statistics: %d waits in %d frames.\n", data_waits, data_tries);
-			data_waits = data_tries = 0;
-		}
-	}
-	duration=jiffies-duration;
-	msg(DBG_TE2,"time to read %d frames: %d jiffies .\n",frame,duration);
-	if (famT_drive)
-	{
-		wait=8;
-		do
-		{
-			sbp_sleep(1);
-			OUT(CDo_sel_i_d,0);
-			i=inb(CDi_status);
-			if (!(i&s_not_data_ready))
-			{
-				OUT(CDo_sel_i_d,1);
-				j=0;
-				do
-				{
-					i=inb(CDi_data);
-					j++;
-					i=inb(CDi_status);
-				}
-				while (!(i&s_not_data_ready));
-				msg(DBG_TEA, "=============too much data (%d bytes)=================.\n", j);
-			}
-			if (!(i&s_not_result_ready))
-			{
-				OUT(CDo_sel_i_d,0);
-				l=0;
-				do
-				{
-					infobuf[l++]=inb(CDi_info);
-					i=inb(CDi_status);
-				}
-				while (!(i&s_not_result_ready));
-				if (infobuf[0]==0x00) success=1;
-#if 1
-				for (j=0;j<l;j++) sprintf(&msgbuf[j*3], " %02X", infobuf[j]);
-				msgbuf[j*3]=0;
-				msg(DBG_TE2,"sbp_data info response:%s\n", msgbuf);
-#endif
-				if (infobuf[0]==0x02)
-				{
-					error_flag++;
-					do
-					{
-						++recursion;
-						if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (sbp_data): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",recursion);
-						else msg(DBG_TEA,"sbp_data: CMDT_READ_ERR necessary.\n");
-						clr_cmdbuf();
-						drvcmd[0]=CMDT_READ_ERR;
-						j=cmd_out_T(); /* !!! recursive here !!! */
-						--recursion;
-						sbp_sleep(1);
-					}
-					while (j<0);
-					D_S[d].error_state=infobuf[2];
-					D_S[d].b3=infobuf[3];
-					D_S[d].b4=infobuf[4];
-				}
-				break;
-			}
-			else
-			{
-#if 0
-				msg(DBG_TEA, "============= waiting for result=================.\n");
-				sbp_sleep(1);
-#endif
-			}
-		}
-		while (wait--);
-	}
-
-	if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
-	{
-		msg(DBG_TEA, "================error flag: %d=================.\n", error_flag);
-		msg(DBG_INF,"sbp_data: read aborted by drive.\n");
-#if 1
-		i=cc_DriveReset(); /* ugly fix to prevent a hang */
-#else
-		i=cc_ReadError();
-#endif
-		return (0);
-	}
-	
-	if (fam0L_drive)
-	{
-		SBPCD_CLI;
-		i=maxtim_data;
-		for (timeout=jiffies+HZ; timeout > jiffies; timeout--)
-		{
-			for ( ;i!=0;i--)
-			{
-				j=inb(CDi_status);
-				if (!(j&s_not_data_ready)) break;
-				if (!(j&s_not_result_ready)) break;
-				if (j&s_attention) break;
-			}
-			if (i != 0 || timeout <= jiffies) break;
-			sbp_sleep(0);
-			i = 1;
-		}
-		if (i==0) msg(DBG_INF,"status timeout after READ.\n");
-		if (!(j&s_attention))
-		{
-			msg(DBG_INF,"sbp_data: timeout waiting DRV_ATTN - retrying.\n");
-			i=cc_DriveReset();  /* ugly fix to prevent a hang */
-			SBPCD_STI;
-			return (0);
-		}
-		SBPCD_STI;
-	}
-	
-#if 0
-	if (!success)
-#endif 0
-		do
-		{
-			if (fam0L_drive) cc_ReadStatus();
-#if 1
-			if (famT_drive) msg(DBG_TE2, "================before ResponseStatus=================.\n", i);
-#endif 1
-			i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
-#if 1
-			if (famT_drive)	msg(DBG_TE2, "================ResponseStatus: %d=================.\n", i);
-#endif 1
-			if (i<0)
-			{
-				msg(DBG_INF,"bad cc_ReadStatus after read: %02X\n", D_S[d].status_bits);
-				return (0);
-			}
-		}
-		while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
-	if (st_check)
-	{
-		i=cc_ReadError();
-		msg(DBG_INF,"cc_ReadError was necessary after read: %d\n",i);
-		return (0);
-	}
-	if (fatal_err)
-	{
-		fatal_err=0;
-		D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;      /* purge buffer */
-		D_S[d].sbp_current = 0;
-		msg(DBG_INF,"sbp_data: fatal_err - retrying.\n");
-		return (0);
-	}
-	
-	D_S[d].sbp_first_frame = CURRENT -> sector / 4;
-	D_S[d].sbp_last_frame = D_S[d].sbp_first_frame + D_S[d].sbp_read_frames - 1;
-	sbp_transfer();
-#if 1
-	if (famT_drive)	msg(DBG_TE2, "================sbp_transfer() done=================.\n");
-#endif 1
-	return (1);
-}
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- *  Open the device special file.  Check that a disk is in. Read TOC.
- */
-static int sbpcd_open(struct inode *ip, struct file *fp)
-{
-	int i;
-	
-	i = MINOR(ip->i_rdev);
-	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
-	{
-		msg(DBG_INF, "open: bad device: %04X\n", ip->i_rdev);
-		return (-ENXIO);             /* no such drive */
-	}
-	if (fp->f_mode & 2)
-		return -EROFS;
-	
-	switch_drive(i);
-	
-	i=cc_ReadError();
-	flags_cmd_out |= f_respo2;
-	cc_ReadStatus();                         /* command: give 1-byte status */
-	i=ResponseStatus();
-	if (famT_drive&&(i<0))
-	{
-		cc_DriveReset();
-		i=ResponseStatus();
-		i=ResponseStatus();
-	}
-	if (i<0)
-	{
-		msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
-		return (-EIO);                  /* drive doesn't respond */
-	}
-	if (famT_drive)	msg(DBG_TE2,"sbpcd_open: ResponseStatus=%02X\n", i);
-	if (!st_door_closed)
-	{
-		if (famT_drive)	msg(DBG_TE2,"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_TE2,"sbpcd_open: !st_spinning.\n");
-			cc_SpinUp();
-			flags_cmd_out |= f_respo2;
-			cc_ReadStatus();
-			i=ResponseStatus();
-		}
-	if (famT_drive)	msg(DBG_TE2,"sbpcd_open: status %02X\n", D_S[d].status_bits);
-	if (!st_door_closed||!st_caddy_in)
-	{
-		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
-		return (-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)
-	{
-		i=LockDoor();
-		D_S[d].open_count=1;
-		if (famT_drive)	msg(DBG_TE2,"sbpcd_open: before i=DiskInfo();.\n");
-		i=DiskInfo();
-		if (famT_drive)	msg(DBG_TE2,"sbpcd_open: after i=DiskInfo();.\n");
-		if ((D_S[d].ored_ctl_adr&0x40)==0)
-			msg(DBG_INF,"CD contains no data tracks.\n");
-	}
-	if (!st_spinning) cc_SpinUp();
-	return (0);
-}
-/*==========================================================================*/
-/*
- *  On close, we flush all sbp blocks from the buffer cache.
- */
-static void sbpcd_release(struct inode * ip, struct file * file)
-{
-	int i;
-	
-	i = MINOR(ip->i_rdev);
-	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
-	{
-		msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev);
-		return;
-	}
-	switch_drive(i);
-	
-	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);
-	
-	/*
-	 * 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 */
-	{
-		if (--D_S[d].open_count<=0) 
-		{
-			i=UnLockDoor();
-			if (D_S[d].audio_state!=audio_playing)
-				if (D_S[d].f_eject) cc_SpinDown();
-			D_S[d].diskstate_flags &= ~cd_size_bit;
-			D_S[d].open_count=0; 
-		}
-	}
-}
-/*==========================================================================*/
-/*
- *
- */
-static struct file_operations sbpcd_fops =
-{
-	NULL,                   /* lseek - default */
-	block_read,             /* read - general block-dev read */
-	block_write,            /* write - general block-dev write */
-	NULL,                   /* readdir - bad */
-	NULL,                   /* select */
-	sbpcd_ioctl,            /* ioctl */
-	NULL,                   /* mmap */
-	sbpcd_open,             /* open */
-	sbpcd_release,          /* release */
-	NULL,                   /* fsync */
-	NULL,                   /* fasync */
-	sbpcd_chk_disk_change,  /* media_change */
-	NULL                    /* revalidate */
-};
-/*==========================================================================*/
-/*
- * accept "kernel command line" parameters 
- * (suggested by Peter MacDonald with SLS 1.03)
- *
- * This is only implemented for the first controller. Should be enough to
- * allow installing with a "strange" distribution kernel.
- *
- * use: tell LILO:
- *                 sbpcd=0x230,SoundBlaster
- *             or
- *                 sbpcd=0x300,LaserMate
- *             or
- *                 sbpcd=0x330,SoundScape
- *
- * (upper/lower case sensitive here - but all-lowercase is ok!!!).
- *
- * the address value has to be the CDROM PORT ADDRESS -
- * not the soundcard base address.
- * For the SPEA/SoundScape setup, DO NOT specify the "configuration port"
- * address, but the address which is really used for the CDROM (usually 8
- * bytes above).
- *
- */
-#if (SBPCD_ISSUE-1)
-static
-#endif
-void sbpcd_setup(const char *s, int *p)
-{
-	setup_done++;
-	msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s);
-	sbpro_type=0; /* default: "LaserMate" */
-	if (p[0]>1) sbpro_type=p[2];
-	if (!strcmp(s,str_sb)) sbpro_type=1;
-	else if (!strcmp(s,str_sb_l)) sbpro_type=1;
-	else if (!strcmp(s,str_sp)) sbpro_type=2;
-	else if (!strcmp(s,str_sp_l)) sbpro_type=2;
-	else if (!strcmp(s,str_ss)) sbpro_type=2;
-	else if (!strcmp(s,str_ss_l)) sbpro_type=2;
-	if (p[0]>0) sbpcd_ioaddr=p[1];
-	
-	CDo_command=sbpcd_ioaddr;
-	CDi_info=sbpcd_ioaddr;
-	CDi_status=sbpcd_ioaddr+1;
-	CDo_sel_i_d=sbpcd_ioaddr+1;
-	CDo_reset=sbpcd_ioaddr+2;
-	CDo_enable=sbpcd_ioaddr+3; 
-	if (sbpro_type==1)
-	{
-		MIXER_addr=sbpcd_ioaddr-0x10+0x04;
-		MIXER_data=sbpcd_ioaddr-0x10+0x05;
-		CDi_data=sbpcd_ioaddr;
-	}
-	else CDi_data=sbpcd_ioaddr+2;
-}
-/*==========================================================================*/
-/*
- * Sequoia S-1000 CD-ROM Interface Configuration
- * as used within SPEA Media FX, Ensonic SoundScape and some Reveal cards
- * The soundcard has to get jumpered for the interface type "Panasonic"
- * (not Sony or Mitsumi) and to get soft-configured for
- *     -> configuration port address
- *     -> CDROM port offset (num_ports): has to be 8 here. Possibly this
- *        offset value determines the interface type (none, Panasonic,
- *        Mitsumi, Sony).
- *        The interface uses a configuration port (0x320, 0x330, 0x340, 0x350)
- *        some bytes below the real CDROM address.
- *         
- *        For the Panasonic style (LaserMate) interface and the configuration
- *        port 0x330, we have to use an offset of 8; so, the real CDROM port
- *        address is 0x338.
- */
-static int config_spea(void)
-{
-	int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
-	/* base address offset between configuration port and CDROM port */
-	int irq_number=0; /* off:0x00, 2:0x01, 7:0x03, 12:0x05, 15:0x07 */
-	int dma_channel=0; /* off: 0x00, 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68 */
-	int dack_polarity=0; /* L:0x00, H:0x80 */
-	int drq_polarity=0x40; /* L:0x00, H:0x40 */
-	int i;
-
-#define SPEA_REG_1 sbpcd_ioaddr-0x08+4
-#define SPEA_REG_2 sbpcd_ioaddr-0x08+5
-	
-	OUT(SPEA_REG_1,0xFF);
-	i=inb(SPEA_REG_1);
-	if (i!=0x0F)
-	{
-		msg(DBG_SEQ,"no SPEA interface at %04X present.\n", sbpcd_ioaddr);
-		return (-1); /* no interface found */
-	}
-	OUT(SPEA_REG_1,0x04);
-	OUT(SPEA_REG_2,0xC0);
-	
-	OUT(SPEA_REG_1,0x05);
-	OUT(SPEA_REG_2,0x10|drq_polarity|dack_polarity);
-	
-#if 1
-#define SPEA_PATTERN 0x80
-#else
-#define SPEA_PATTERN 0x00
-#endif
-	OUT(SPEA_REG_1,0x06);
-	OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
-	OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
-	
-	OUT(SPEA_REG_1,0x09);
-	i=(inb(SPEA_REG_2)&0xCF)|n_ports;
-	OUT(SPEA_REG_2,i);
-	
-	sbpro_type = 0; /* acts like a LaserMate interface now */
-	msg(DBG_SEQ,"found SoundScape interface at %04X.\n", sbpcd_ioaddr);
-	return (0);
-}
-/*==========================================================================*/
-/*
- *  Test for presence of drive and initialize it.  Called at boot time.
- */
-#ifdef MODULE
-int init_module(void)
-#else
-int SBPCD_INIT(void)
-#endif MODULE
-{
-	int i=0, j=0;
-	int addr[2]={1, CDROM_PORT};
-	int port_index;
-	
-	sti();
-	
-	msg(DBG_INF,"sbpcd.c %s\n", VERSION);
-#ifndef MODULE
-#if DISTRIBUTION
-	if (!setup_done)
-	{
-		msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, Longshine, TEAC CD-ROM drives\n");
-		msg(DBG_INF,"= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
-		msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an NE2000 card).\n");
-		msg(DBG_INF,"If that happens, you have to reboot and use the\n");
-		msg(DBG_INF,"LILO (kernel) command line feature like:\n");
-		msg(DBG_INF,"   LILO boot: ... sbpcd=0x230,SoundBlaster\n");
-		msg(DBG_INF,"or like:\n");
-		msg(DBG_INF,"   LILO boot: ... sbpcd=0x300,LaserMate\n");
-		msg(DBG_INF,"or like:\n");
-		msg(DBG_INF,"   LILO boot: ... sbpcd=0x338,SoundScape\n");
-		msg(DBG_INF,"with your REAL address.\n");
-		msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = == = = =\n");
-	}
-#endif DISTRIBUTION
-	sbpcd[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
-	sbpcd[1]=sbpro_type; /* possibly changed by kernel command line */
-#endif MODULE
-	
-	for (port_index=0;port_index<NUM_PROBE;port_index+=2)
-	{
-		addr[1]=sbpcd[port_index];
-		if (addr[1]==0) break;
-		if (check_region(addr[1],4))
-		{
-			msg(DBG_INF,"check_region: %03X is not free.\n",addr[1]);
-			continue;
-		}
-		if (sbpcd[port_index+1]==2) type=str_sp;
-		else if (sbpcd[port_index+1]==1) type=str_sb;
-		else type=str_lm;
-		sbpcd_setup(type, addr);
-#if DISTRIBUTION
-		msg(DBG_INF,"Scanning 0x%X (%s)...\n", CDo_command, type);
-#endif DISTRIBUTION
-		if (sbpcd[port_index+1]==2)
-		{
-			i=config_spea();
-			if (i<0) continue;
-		}
-#ifdef PATH_CHECK
-		if (check_card(addr[1])) continue;
-#endif PATH_CHECK
-		i=check_drives();
-		msg(DBG_INI,"check_drives done.\n");
-		if (i>=0) break; /* drive found */
-	} /* end of cycling through the set of possible I/O port addresses */
-	
-	if (ndrives==0)
-	{
-		msg(DBG_INF, "No drive found.\n");
-#ifdef MODULE
-		return -EIO;
-#else
-		goto init_done;
-#endif MODULE
-	}
-	
-	if (port_index>0)
-		msg(DBG_INF, "You should configure sbpcd.h for your hardware.\n");
-	check_datarate();
-	msg(DBG_INI,"check_datarate done.\n");
-	
-#if 0
-	if (!famL_drive)
-	{
-		OUT(CDo_reset,0);
-		sbp_sleep(HZ);
-	}
-#endif 0
-
-	for (j=0;j<NR_SBPCD;j++)
-	{
-		if (D_S[j].drv_id==-1) continue;
-		switch_drive(j);
-#if 1
-		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;
-#if EJECT
-		if (!fam0_drive) D_S[d].f_eject=1;
-		else D_S[d].f_eject=0;
-#else
-		D_S[d].f_eject=0;
-#endif
-		
-		cc_ReadStatus();
-		i=ResponseStatus();  /* returns orig. status or p_busy_new */
-		if (famT_drive) i=ResponseStatus();  /* returns orig. status or p_busy_new */
-		if (i<0)
-			if (i!=-402)
-				msg(DBG_INF,"init: ResponseStatus returns %d.\n",i);
-		else
-		{
-			if (st_check)
-			{
-				i=cc_ReadError();
-				msg(DBG_INI,"init: cc_ReadError returns %d\n",i);
-			}
-		}
-		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)
-		{
-			timeout=jiffies+2*HZ;
-			do
-			{
-				i=GetStatus();
-				msg(DBG_INI,"init: second GetStatus: %02X\n",i);
-				msg(DBG_LCS,
-				    "init: second GetStatus: error_byte=%d\n",
-				    D_S[d].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;
-	}
-	
-	/*
-	 * Turn on the CD audio channels.
-	 * For "compatible" soundcards (with "SBPRO 0" or "SBPRO 2"), the addresses
-	 * are obtained from SOUND_BASE (see sbpcd.h).
-	 */
-	if ((sbpro_type==1) || (SOUND_BASE))
-	{
-		if (sbpro_type!=1)
-		{
-			MIXER_addr=SOUND_BASE+0x04; /* sound card's address register */
-			MIXER_data=SOUND_BASE+0x05; /* sound card's data register */
-		}
-		OUT(MIXER_addr,MIXER_CD_Volume); /* select SB Pro mixer register */
-		OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
-	}
-	
-	if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0)
-	{
-		msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
-#ifdef MODULE
-		return -EIO;
-#else
-		goto init_done;
-#endif MODULE
-	}
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
-	
-	request_region(CDo_command,4,major_name);
-	
-	for (j=0;j<NR_SBPCD;j++)
-	{
-		if (D_S[j].drv_id==-1) continue;
-		switch_drive(j);
-		/*
-		 * allocate memory for the frame buffers
-		 */
-		D_S[j].aud_buf=NULL;
-		D_S[j].sbp_audsiz=0;
-		D_S[j].sbp_bufsiz=SBP_BUFFER_FRAMES;
-		if (D_S[j].drv_type&drv_fam1)
-			if (READ_AUDIO>0) D_S[j].sbp_audsiz=READ_AUDIO;
-		D_S[j].sbp_buf=(u_char *) vmalloc(D_S[j].sbp_bufsiz*CD_FRAMESIZE);
-		if (D_S[j].sbp_buf==NULL)
-		{
-			msg(DBG_INF,"data buffer (%d frames) not available.\n",D_S[j].sbp_bufsiz);
-			return -EIO;
-		}
-		msg(DBG_INF,"data buffer size: %d frames.\n",SBP_BUFFER_FRAMES);
-		if (D_S[j].sbp_audsiz>0)
-		{
-			D_S[j].aud_buf=(u_char *) vmalloc(D_S[j].sbp_audsiz*CD_FRAMESIZE_RAW);
-			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);
-		}
-		/*
-		 * set the block size
-		 */
-		sbpcd_blocksizes[j]=CD_FRAMESIZE;
-	}
-	blksize_size[MAJOR_NR]=sbpcd_blocksizes;
-	
-#ifdef MODULE
-	return (0);
-#else
- init_done:
-#if !(SBPCD_ISSUE-1)
-#ifdef CONFIG_SBPCD2
-	sbpcd2_init();
-#endif
-#ifdef CONFIG_SBPCD3
-	sbpcd3_init();
-#endif
-#ifdef CONFIG_SBPCD4
-	sbpcd4_init();
-#endif
-#endif
-	return 0;
-#endif MODULE
-}
-/*==========================================================================*/
-#ifdef MODULE
-void cleanup_module(void)
-{
-	int j;
-	
-	if (MOD_IN_USE)
-	{
-		msg(DBG_INF, "%s module in use - can't remove it.\n", major_name);
-		return;
-	}
-	if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
-	{
-		msg(DBG_INF, "What's that: can't unregister %s.\n", major_name);
-		return;
-	}
-	release_region(CDo_command,4);
-	
-	for (j=0;j<NR_SBPCD;j++)
-	{
-		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);
-	}
-	msg(DBG_INF, "%s module released.\n", major_name);
-}
-#endif MODULE
-/*==========================================================================*/
-/*
- * Check if the media has changed in the CD-ROM drive.
- * used externally (isofs/inode.c, fs/buffer.c)
- * Currently disabled (has to get "synchronized").
- */
-static int sbpcd_chk_disk_change(kdev_t full_dev)
-{
-	int i, st;
-	
-	msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev));
-	return (0); /* "busy" test necessary before we really can check */
-	
-	i=MINOR(full_dev);
-	if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) )
-	{
-		msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev);
-		return (-1);
-	}
-	
-	switch_drive(i);
-	
-	cc_ReadStatus();                         /* command: give 1-byte status */
-	st=ResponseStatus();
-	msg(DBG_CHK,"media_check: %02X\n",D_S[d].status_bits);
-	if (st<0)
-	{
-		msg(DBG_INF,"media_check: ResponseStatus error.\n");
-		return (1); /* status not obtainable */
-	}
-	if (D_S[d].CD_changed==0xFF) msg(DBG_CHK,"media_check: \"changed\" assumed.\n");
-	if (!st_spinning) msg(DBG_CHK,"media_check: motor off.\n");
-	if (!st_door_closed)
-	{
-		msg(DBG_CHK,"media_check: door open.\n");
-		D_S[d].CD_changed=0xFF;
-	}
-	if (!st_caddy_in)
-	{
-		msg(DBG_CHK,"media_check: no disk in drive.\n");
-		D_S[d].open_count=0;
-		D_S[d].CD_changed=0xFF;
-	}
-	if (!st_diskok) msg(DBG_CHK,"media_check: !st_diskok.\n");
-	
-#if 0000
-	if (D_S[d].CD_changed==0xFF)
-	{
-		D_S[d].CD_changed=1;
-		return (1); /* driver had a change detected before */
-	}
-#endif 0000 /* seems to give additional errors at the moment */
-	
-	if (!st_diskok) return (1); /* disk not o.k. */
-	if (!st_caddy_in) return (1); /* disk removed */
-	if (!st_door_closed) return (1); /* door open */
-	return (0);
-}
-/*==========================================================================*/
-/*
- * 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 v1.3.35/linux/drivers/block/sbpcd2.c linux/drivers/block/sbpcd2.c
--- v1.3.35/linux/drivers/block/sbpcd2.c	Fri Jan 13 09:02:32 1995
+++ linux/drivers/block/sbpcd2.c	Thu Jan  1 02:00:00 1970
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 2
-#include "sbpcd.c"
diff -u --recursive --new-file v1.3.35/linux/drivers/block/sbpcd3.c linux/drivers/block/sbpcd3.c
--- v1.3.35/linux/drivers/block/sbpcd3.c	Fri Jan 13 09:02:32 1995
+++ linux/drivers/block/sbpcd3.c	Thu Jan  1 02:00:00 1970
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 3
-#include "sbpcd.c"
diff -u --recursive --new-file v1.3.35/linux/drivers/block/sbpcd4.c linux/drivers/block/sbpcd4.c
--- v1.3.35/linux/drivers/block/sbpcd4.c	Fri Jan 13 09:02:32 1995
+++ linux/drivers/block/sbpcd4.c	Thu Jan  1 02:00:00 1970
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 4
-#include "sbpcd.c"
diff -u --recursive --new-file v1.3.35/linux/drivers/block/sjcd.c linux/drivers/block/sjcd.c
--- v1.3.35/linux/drivers/block/sjcd.c	Mon Oct 16 18:38:22 1995
+++ linux/drivers/block/sjcd.c	Thu Jan  1 02:00:00 1970
@@ -1,1915 +0,0 @@
-/* -- sjcd.c
- *
- *   Sanyo CD-ROM device driver implementation, Version 1.5
- *   Copyright (C) 1995  Vadim V. Model
- *
- *   model@cecmow.enet.dec.com
- *   vadim@rbrf.ru
- *   vadim@ipsun.ras.ru
- *
- *   ISP16 detection and configuration.
- *   Copyright (C) 1995  Eric van der Maarel (maarel@marin.nl)
- *                   and Vadim Model (vadim@cecmow.enet.dec.com)
- *
- *
- *  This driver is based on pre-works by Eberhard Moenkeberg (emoenke@gwdg.de);
- *  it was developed under use of mcd.c from Martin Harriss, with help of
- *  Eric van der Maarel (maarel@marin.nl).
- *
- *  ISP16 detection and configuration by Eric van der Maarel (maarel@marin.nl).
- *  Sound configuration by Vadim V. Model (model@cecmow.enet.dec.com)
- *
- *  It is planned to include these routines into sbpcd.c later - to make
- *  a "mixed use" on one cable possible for all kinds of drives which use
- *  the SoundBlaster/Panasonic style CDROM interface. But today, the
- *  ability to install directly from CDROM is more important than flexibility.
- *
- *  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:
- *  1.1 First public release with kernel version 1.3.7.
- *      Written by Vadim Model.
- *  1.2 Added detection and configuration of cdrom interface
- *      on ISP16 soundcard.
- *      Allow for command line options: sjcd=<io_base>,<irq>,<dma>
- *  1.3 Some minor changes to README.sjcd.
- *  1.4 MSS Sound support!! Listen to a CD through the speakers.
- *  1.5 Module support and bugfixes.
- *      Tray locking.
- *
- */
-
-#include <linux/major.h>
-#include <linux/config.h>
-
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#define sjcd_init init_module
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[]= UTS_RELEASE;
-#endif
-#endif
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#define MAJOR_NR SANYO_CDROM_MAJOR
-#include "blk.h"
-#include <linux/sjcd.h>
-
-/* 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 due to soundcard conflicts */
-#define ISP16_IRQ_7  0x08  /* shouldn't be used due to soundcard 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 */
-
-static short isp16_detect(void);
-static short isp16_c928__detect(void);
-static short isp16_c929__detect(void);
-static short isp16_cdi_config( int base, u_char drive_type, int irq, int dma );
-static void isp16_sound_config( void );
-static short isp16_type; /* dependent on type of interface card */
-static u_char isp16_ctrl;
-static u_short isp16_enable_port;
-
-static int sjcd_present = 0;
-static u_char special_mask = 0;
-
-static unsigned char defaults[ 16 ] = {
-  0xA8, 0xA8, 0x18, 0x18, 0x18, 0x18, 0x8E, 0x8E,
-  0x03, 0x00, 0x02, 0x00, 0x0A, 0x00, 0x00, 0x00
-};
-
-#define SJCD_BUF_SIZ 32 /* cdr-h94a has internal 64K buffer */
-
-/*
- * buffer for block size conversion
- */
-static char sjcd_buf[ 2048 * SJCD_BUF_SIZ ];
-static volatile int sjcd_buf_bn[ SJCD_BUF_SIZ ], sjcd_next_bn;
-static volatile int sjcd_buf_in, sjcd_buf_out = -1;
-
-/*
- * Status.
- */
-static unsigned short sjcd_status_valid = 0;
-static unsigned short sjcd_door_closed;
-static unsigned short sjcd_door_was_open;
-static unsigned short sjcd_media_is_available;
-static unsigned short sjcd_media_is_changed;
-static unsigned short sjcd_toc_uptodate = 0;
-static unsigned short sjcd_command_failed;
-static volatile unsigned char sjcd_completion_status = 0;
-static volatile unsigned char sjcd_completion_error = 0;
-static unsigned short sjcd_command_is_in_progress = 0;
-static unsigned short sjcd_error_reported = 0;
-
-static int sjcd_open_count;
-
-static int sjcd_audio_status;
-static struct sjcd_play_msf sjcd_playing;
-
-static int sjcd_port = SJCD_BASE_ADDR;
-static int sjcd_irq  = SJCD_INTR_NR;
-static int sjcd_dma  = SJCD_DMA_NR;
-
-static struct wait_queue *sjcd_waitq = NULL;
-
-/*
- * Data transfer.
- */
-static volatile unsigned short sjcd_transfer_is_active = 0;
-
-enum sjcd_transfer_state {
-  SJCD_S_IDLE     = 0,
-  SJCD_S_START    = 1,
-  SJCD_S_MODE     = 2,
-  SJCD_S_READ     = 3,
-  SJCD_S_DATA     = 4,
-  SJCD_S_STOP     = 5,
-  SJCD_S_STOPPING = 6
-};
-static enum sjcd_transfer_state sjcd_transfer_state = SJCD_S_IDLE;
-static long sjcd_transfer_timeout = 0;
-static int sjcd_read_count = 0;
-static unsigned char sjcd_mode = 0;
-
-#define SJCD_READ_TIMEOUT 5000
-
-#if defined( SJCD_GATHER_STAT )
-/*
- * Statistic.
- */
-static struct sjcd_stat statistic;
-#endif
-
-/*
- * Timer.
- */
-static struct timer_list sjcd_delay_timer = { NULL, NULL, 0, 0, NULL };
-
-#define SJCD_SET_TIMER( func, tmout )           \
-    ( sjcd_delay_timer.expires = jiffies+tmout,         \
-      sjcd_delay_timer.function = ( void * )func, \
-      add_timer( &sjcd_delay_timer ) )
-
-#define CLEAR_TIMER del_timer( &sjcd_delay_timer )
-
-/*
- * Set up device, i.e., use command line data to set
- * base address, irq and dma.
- */
-void sjcd_setup( char *str, int *ints )
-{
-   if (ints[0] > 0)
-      sjcd_port = ints[1];
-   if (ints[0] > 1)      
-      sjcd_irq = ints[2];
-   if (ints[0] > 2)      
-      sjcd_dma = ints[3];
-}
-
-/*
- * Special converters.
- */
-static unsigned char bin2bcd( int bin ){
-  int u, v;
-
-  u = bin % 10; v = bin / 10;
-  return( u | ( v << 4 ) );
-}
-
-static int bcd2bin( unsigned char bcd ){
-    return( ( bcd >> 4 ) * 10 + ( bcd & 0x0F ) );
-}
-
-static long msf2hsg( struct msf *mp ){
-  return( bcd2bin( mp->frame ) + bcd2bin( mp->sec ) * 75
-	 + bcd2bin( mp->min ) * 4500 - 150 );
-}
-
-static void hsg2msf( long hsg, struct msf *msf ){
-  hsg += 150; msf->min = hsg / 4500;
-  hsg %= 4500; msf->sec = hsg / 75; msf->frame = hsg % 75;
-  msf->min = bin2bcd( msf->min );       /* convert to BCD */
-  msf->sec = bin2bcd( msf->sec );
-  msf->frame = bin2bcd( msf->frame );
-}
-
-/*
- * Send a command to cdrom. Invalidate status.
- */
-static void sjcd_send_cmd( unsigned char cmd ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: send_cmd( 0x%x )\n", cmd );
-#endif
-  outb( cmd, SJCDPORT( 0 ) );
-  sjcd_command_is_in_progress = 1;
-  sjcd_status_valid = 0;
-  sjcd_command_failed = 0;
-}
-
-/*
- * Send a command with one arg to cdrom. Invalidate status.
- */
-static void sjcd_send_1_cmd( unsigned char cmd, unsigned char a ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: send_1_cmd( 0x%x, 0x%x )\n", cmd, a );
-#endif
-  outb( cmd, SJCDPORT( 0 ) );
-  outb( a, SJCDPORT( 0 ) );
-  sjcd_command_is_in_progress = 1;
-  sjcd_status_valid = 0;
-  sjcd_command_failed = 0;
-}
-
-/*
- * Send a command with four args to cdrom. Invalidate status.
- */
-static void sjcd_send_4_cmd( unsigned char cmd, unsigned char a,
-	    unsigned char b, unsigned char c, unsigned char d ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: send_4_cmd( 0x%x )\n", cmd );
-#endif
-  outb( cmd, SJCDPORT( 0 ) );
-  outb( a, SJCDPORT( 0 ) );
-  outb( b, SJCDPORT( 0 ) );
-  outb( c, SJCDPORT( 0 ) );
-  outb( d, SJCDPORT( 0 ) );
-  sjcd_command_is_in_progress = 1;
-  sjcd_status_valid = 0;
-  sjcd_command_failed = 0;
-}
-
-/*
- * Send a play or read command to cdrom. Invalidate Status.
- */
-static void sjcd_send_6_cmd( unsigned char cmd, struct sjcd_play_msf *pms ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: send_long_cmd( 0x%x )\n", cmd );
-#endif
-  outb( cmd, SJCDPORT( 0 ) );
-  outb( pms->start.min,   SJCDPORT( 0 ) );
-  outb( pms->start.sec,   SJCDPORT( 0 ) );
-  outb( pms->start.frame, SJCDPORT( 0 ) );
-  outb( pms->end.min,     SJCDPORT( 0 ) );
-  outb( pms->end.sec,     SJCDPORT( 0 ) );
-  outb( pms->end.frame,   SJCDPORT( 0 ) );
-  sjcd_command_is_in_progress = 1;
-  sjcd_status_valid = 0;
-  sjcd_command_failed = 0;
-}
-
-/*
- * Get a value from the data port. Should not block, so we use a little
- * wait for a while. Returns 0 if OK.
- */
-static int sjcd_load_response( void *buf, int len ){
-  unsigned char *resp = ( unsigned char * )buf;
-
-  for( ; len; --len ){ 
-    int i;
-    for( i = 200; i-- && !SJCD_STATUS_AVAILABLE( inb( SJCDPORT( 1 ) ) ); );
-    if( i > 0 ) *resp++ = ( unsigned char )inb( SJCDPORT( 0 ) );
-    else break;
-  }
-  return( len );
-}
-
-/*
- * Load and parse command completion status (drive info byte and maybe error).
- * Sorry, no error classification yet.
- */
-static void sjcd_load_status( void ){
-  sjcd_media_is_changed = 0;
-  sjcd_completion_error = 0;
-  sjcd_completion_status = inb( SJCDPORT( 0 ) );
-  if( sjcd_completion_status & SST_DOOR_OPENED ){
-    sjcd_door_closed = sjcd_media_is_available = 0;
-  } else {
-    sjcd_door_closed = 1;
-    if( sjcd_completion_status & SST_MEDIA_CHANGED )
-      sjcd_media_is_available = sjcd_media_is_changed = 1;
-    else if( sjcd_completion_status & 0x0F ){
-      /*
-       * OK, we seem to catch an error ...
-       */
-      while( !SJCD_STATUS_AVAILABLE( inb( SJCDPORT( 1 ) ) ) );
-      sjcd_completion_error = inb( SJCDPORT( 0 ) );
-      if( ( sjcd_completion_status & 0x08 ) &&
-	 ( sjcd_completion_error & 0x40 ) )
-	sjcd_media_is_available = 0;
-      else sjcd_command_failed = 1;
-    } else sjcd_media_is_available = 1;
-  }
-  /*
-   * Ok, status loaded successfully.
-   */
-  sjcd_status_valid = 1, sjcd_error_reported = 0;
-  sjcd_command_is_in_progress = 0;
-
-  /*
-   * If the disk is changed, the TOC is not valid.
-   */
-  if( sjcd_media_is_changed ) sjcd_toc_uptodate = 0;
-#if defined( SJCD_TRACE )
-  printk( "sjcd: status %02x.%02x loaded.\n",
-	 ( int )sjcd_completion_status, ( int )sjcd_completion_error );
-#endif
-}
-
-/*
- * Read status from cdrom. Check to see if the status is available.
- */
-static int sjcd_check_status( void ){
-  /*
-   * Try to load the response from cdrom into buffer.
-   */
-  if( SJCD_STATUS_AVAILABLE( inb( SJCDPORT( 1 ) ) ) ){
-    sjcd_load_status();
-    return( 1 );
-  } else {
-    /*
-     * No status is available.
-     */
-    return( 0 );
-  }
-}
-
-/*
- * This is just timout counter, and nothing more. Surprized ? :-)
- */
-static volatile long sjcd_status_timeout;
-
-/*
- * We need about 10 seconds to wait. The longest command takes about 5 seconds
- * to probe the disk (usually after tray closed or drive reset). Other values
- * should be thought of for other commands.
- */
-#define SJCD_WAIT_FOR_STATUS_TIMEOUT 1000
-
-static void sjcd_status_timer( void ){
-  if( sjcd_check_status() ){
-    /*
-     * The command completed and status is loaded, stop waiting.
-     */
-    wake_up( &sjcd_waitq );
-  } else if( --sjcd_status_timeout <= 0 ){
-    /*
-     * We are timed out. 
-     */
-    wake_up( &sjcd_waitq );
-  } else {
-    /*
-     * We have still some time to wait. Try again.
-     */
-    SJCD_SET_TIMER( sjcd_status_timer, 1 );
-  }
-}
-
-/*
- * Wait for status for 10 sec approx. Returns non-positive when timed out.
- * Should not be used while reading data CDs.
- */
-static int sjcd_wait_for_status( void ){
-  sjcd_status_timeout = SJCD_WAIT_FOR_STATUS_TIMEOUT;
-  SJCD_SET_TIMER( sjcd_status_timer, 1 ); 
-  sleep_on( &sjcd_waitq );    
-#if defined( SJCD_DIAGNOSTIC ) || defined ( SJCD_TRACE )
-  if( sjcd_status_timeout <= 0 )
-    printk( "sjcd: Error Wait For Status.\n" );
-#endif
-  return( sjcd_status_timeout );
-}
-
-static int sjcd_receive_status( void ){
-  int i;
-#if defined( SJCD_TRACE )
-  printk( "sjcd: receive_status\n" );
-#endif
-  /*
-   * Wait a bit for status available.
-   */
-  for( i = 200; i-- && ( sjcd_check_status() == 0 ); );
-  if( i < 0 ){
-#if defined( SJCD_TRACE )
-    printk( "sjcd: long wait for status\n" );
-#endif
-    if( sjcd_wait_for_status() <= 0 )
-      printk( "sjcd: Timeout when read status.\n" );
-    else i = 0;
-  }
-  return( i );
-}
-
-/*
- * Load the status. Issue get status command and wait for status available.
- */
-static void sjcd_get_status( void ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: get_status\n" );
-#endif
-  sjcd_send_cmd( SCMD_GET_STATUS );
-  sjcd_receive_status();
-}
-
-/*
- * Check the drive if the disk is changed. Should be revised.
- */
-static int sjcd_disk_change( kdev_t full_dev ){
-#if 0
-  printk( "sjcd_disk_change( 0x%x )\n", full_dev );
-#endif
-  if( MINOR( full_dev ) > 0 ){
-    printk( "sjcd: request error: invalid device minor.\n" );
-    return 0;
-  }
-  if( !sjcd_command_is_in_progress )
-    sjcd_get_status();
-  return( sjcd_status_valid ? sjcd_media_is_changed : 0 );
-}
-
-/*
- * Read the table of contents (TOC) and TOC header if necessary.
- * We assume that the drive contains no more than 99 toc entries.
- */
-static struct sjcd_hw_disk_info sjcd_table_of_contents[ SJCD_MAX_TRACKS ];
-static unsigned char sjcd_first_track_no, sjcd_last_track_no;
-#define sjcd_disk_length  sjcd_table_of_contents[0].un.track_msf
-
-static int sjcd_update_toc( void ){
-  struct sjcd_hw_disk_info info;
-  int i;
-#if defined( SJCD_TRACE )
-  printk( "sjcd: update toc:\n" );
-#endif
-  /*
-   * check to see if we need to do anything
-   */
-  if( sjcd_toc_uptodate ) return( 0 );
-
-  /*
-   * Get the TOC start information.
-   */
-  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_1_TRACK );
-  sjcd_receive_status();
-
-  if( !sjcd_status_valid ){
-    printk( "cannot load status.\n" );
-    return( -1 );
-  }
-
-  if( !sjcd_media_is_available ){
-    printk( "no disk in drive\n" );
-    return( -1 );
-  }
-
-  if( !sjcd_command_failed ){
-    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
-      printk( "cannot load response about TOC start.\n" );
-      return( -1 );
-    }
-    sjcd_first_track_no = bcd2bin( info.un.track_no );
-  } else {
-    printk( "get first failed\n" );
-    return( -1 );
-  }
-#if defined( SJCD_TRACE )
-  printk( "TOC start 0x%02x ", sjcd_first_track_no );
-#endif
-  /*
-   * Get the TOC finish information.
-   */
-  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_L_TRACK );
-  sjcd_receive_status();
-
-  if( !sjcd_status_valid ){
-    printk( "cannot load status.\n" );
-    return( -1 );
-  }
-
-  if( !sjcd_media_is_available ){
-    printk( "no disk in drive\n" );
-    return( -1 );
-  }
-
-  if( !sjcd_command_failed ){
-    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
-      printk( "cannot load response about TOC finish.\n" );
-      return( -1 );
-    }
-    sjcd_last_track_no = bcd2bin( info.un.track_no );
-  } else {
-    printk( "get last failed\n" );
-    return( -1 );
-  }
-#if defined( SJCD_TRACE )
-  printk( "TOC finish 0x%02x ", sjcd_last_track_no );
-#endif
-  for( i = sjcd_first_track_no; i <= sjcd_last_track_no; i++ ){
-    /*
-     * Get the first track information.
-     */
-    sjcd_send_1_cmd( SCMD_GET_DISK_INFO, bin2bcd( i ) );
-    sjcd_receive_status();
-
-    if( !sjcd_status_valid ){
-      printk( "cannot load status.\n" );
-      return( -1 );
-    }
-
-    if( !sjcd_media_is_available ){
-      printk( "no disk in drive\n" );
-      return( -1 );
-    }
-
-    if( !sjcd_command_failed ){
-      if( sjcd_load_response( &sjcd_table_of_contents[ i ],
-			     sizeof( struct sjcd_hw_disk_info ) ) != 0 ){
-	printk( "cannot load info for %d track\n", i );
-	return( -1 );
-      }
-    } else {
-      printk( "get info %d failed\n", i );
-      return( -1 );
-    }
-  }
-
-  /*
-   * Get the disk lenght info.
-   */
-  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_D_SIZE );
-  sjcd_receive_status();
-
-  if( !sjcd_status_valid ){
-    printk( "cannot load status.\n" );
-    return( -1 );
-  }
-
-  if( !sjcd_media_is_available ){
-    printk( "no disk in drive\n" );
-    return( -1 );
-  }
-
-  if( !sjcd_command_failed ){
-    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
-      printk( "cannot load response about disk size.\n" );
-      return( -1 );
-    }
-    sjcd_disk_length.min = info.un.track_msf.min;
-    sjcd_disk_length.sec = info.un.track_msf.sec;
-    sjcd_disk_length.frame = info.un.track_msf.frame;
-  } else {
-    printk( "get size failed\n" );
-    return( 1 );
-  }
-#if defined( SJCD_TRACE )
-  printk( "(%02x:%02x.%02x)\n", sjcd_disk_length.min,
-	 sjcd_disk_length.sec, sjcd_disk_length.frame );
-#endif
-  return( 0 );
-}
-
-/*
- * Load subchannel information.
- */
-static int sjcd_get_q_info( struct sjcd_hw_qinfo *qp ){
-  int s;
-#if defined( SJCD_TRACE )
-  printk( "sjcd: load sub q\n" );
-#endif
-  sjcd_send_cmd( SCMD_GET_QINFO );
-  s = sjcd_receive_status();
-  if( s < 0 || sjcd_command_failed || !sjcd_status_valid ){
-    sjcd_send_cmd( 0xF2 );
-    s = sjcd_receive_status();
-    if( s < 0 || sjcd_command_failed || !sjcd_status_valid ) return( -1 );
-    sjcd_send_cmd( SCMD_GET_QINFO );
-    s = sjcd_receive_status();
-    if( s < 0 || sjcd_command_failed || !sjcd_status_valid ) return( -1 );
-  }
-  if( sjcd_media_is_available )
-    if( sjcd_load_response( qp, sizeof( *qp ) ) == 0 ) return( 0 );
-  return( -1 );
-}
-
-/*
- * Start playing from the specified position.
- */
-static int sjcd_play( struct sjcd_play_msf *mp ){
-  struct sjcd_play_msf msf;
-
-  /*
-   * Turn the device to play mode.
-   */
-  sjcd_send_1_cmd( SCMD_SET_MODE, SCMD_MODE_PLAY );
-  if( sjcd_receive_status() < 0 ) return( -1 );
-
-  /*
-   * Seek to the starting point.
-   */
-  msf.start = mp->start;
-  msf.end.min = msf.end.sec = msf.end.frame = 0x00;
-  sjcd_send_6_cmd( SCMD_SEEK, &msf );
-  if( sjcd_receive_status() < 0 ) return( -1 );
-
-  /*
-   * Start playing.
-   */
-  sjcd_send_6_cmd( SCMD_PLAY, mp );
-  return( sjcd_receive_status() );
-}
-
-/*
- * Tray control functions.
- */
-static int sjcd_tray_close( void ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: tray_close\n" );
-#endif
-  sjcd_send_cmd( SCMD_CLOSE_TRAY );
-  return( sjcd_receive_status() );
-}
-
-static int sjcd_tray_lock( void ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: tray_lock\n" );
-#endif
-  sjcd_send_cmd( SCMD_LOCK_TRAY );
-  return( sjcd_receive_status() );
-}
-
-static int sjcd_tray_unlock( void ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: tray_unlock\n" );
-#endif
-  sjcd_send_cmd( SCMD_UNLOCK_TRAY );
-  return( sjcd_receive_status() );
-}
-
-static int sjcd_tray_open( void ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: tray_open\n" );
-#endif
-  sjcd_send_cmd( SCMD_EJECT_TRAY );
-  return( sjcd_receive_status() );
-}
-
-/*
- * Do some user commands.
- */
-static int sjcd_ioctl( struct inode *ip, struct file *fp,
-		       unsigned int cmd, unsigned long arg ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd:ioctl\n" );
-#endif
-
-  if( ip == NULL ) return( -EINVAL );
-
-  sjcd_get_status();
-  if( !sjcd_status_valid ) return( -EIO );
-  if( sjcd_update_toc() < 0 ) return( -EIO );
-
-  switch( cmd ){
-  case CDROMSTART:{
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: start\n" );
-#endif
-    return( 0 );
-  }
-
-  case CDROMSTOP:{
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: stop\n" );
-#endif
-    sjcd_send_cmd( SCMD_PAUSE );
-    ( void )sjcd_receive_status();
-    sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
-    return( 0 );
-  }
-
-  case CDROMPAUSE:{
-    struct sjcd_hw_qinfo q_info;
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: pause\n" );
-#endif
-    if( sjcd_audio_status == CDROM_AUDIO_PLAY ){
-      sjcd_send_cmd( SCMD_PAUSE );
-      ( void )sjcd_receive_status();
-      if( sjcd_get_q_info( &q_info ) < 0 ){
-	sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
-      } else {
-	sjcd_audio_status = CDROM_AUDIO_PAUSED;
-	sjcd_playing.start = q_info.abs;
-      }
-      return( 0 );
-    } else return( -EINVAL );
-  }
-
-  case CDROMRESUME:{
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: resume\n" );
-#endif
-    if( sjcd_audio_status == CDROM_AUDIO_PAUSED ){
-      /*
-       * continue play starting at saved location
-       */
-      if( sjcd_play( &sjcd_playing ) < 0 ){
-	sjcd_audio_status = CDROM_AUDIO_ERROR;
-	return( -EIO );
-      } else {
-	sjcd_audio_status = CDROM_AUDIO_PLAY;
-	return( 0 );
-      }
-    } else return( -EINVAL );
-  }
-
-  case CDROMPLAYTRKIND:{
-    struct cdrom_ti ti; int s;
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: playtrkind\n" );
-#endif
-    if( ( s = verify_area( VERIFY_READ, (void *)arg, sizeof( ti ) ) ) == 0 ){
-      memcpy_fromfs( &ti, (void *)arg, sizeof( ti ) );
-
-      if( ti.cdti_trk0 < sjcd_first_track_no ) return( -EINVAL );
-      if( ti.cdti_trk1 > sjcd_last_track_no )
-	ti.cdti_trk1 = sjcd_last_track_no;
-      if( ti.cdti_trk0 > ti.cdti_trk1 ) return( -EINVAL );
-
-      sjcd_playing.start = sjcd_table_of_contents[ ti.cdti_trk0 ].un.track_msf;
-      sjcd_playing.end = ( ti.cdti_trk1 < sjcd_last_track_no ) ?
-	sjcd_table_of_contents[ ti.cdti_trk1 + 1 ].un.track_msf :
-	  sjcd_table_of_contents[ 0 ].un.track_msf;
-      
-      if( sjcd_play( &sjcd_playing ) < 0 ){
-	sjcd_audio_status = CDROM_AUDIO_ERROR;
-	return( -EIO );
-      } else sjcd_audio_status = CDROM_AUDIO_PLAY;
-    }
-    return( s );
-  }
-
-  case CDROMPLAYMSF:{
-    struct cdrom_msf sjcd_msf; int s;
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: playmsf\n" );
-#endif
-    if( ( s = verify_area( VERIFY_READ, (void *)arg, sizeof( sjcd_msf ) ) ) == 0 ){
-      if( sjcd_audio_status == CDROM_AUDIO_PLAY ){
-	sjcd_send_cmd( SCMD_PAUSE );
-	( void )sjcd_receive_status();
-	sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
-      }
-
-      memcpy_fromfs( &sjcd_msf, (void *)arg, sizeof( sjcd_msf ) );
-
-      sjcd_playing.start.min = bin2bcd( sjcd_msf.cdmsf_min0 );
-      sjcd_playing.start.sec = bin2bcd( sjcd_msf.cdmsf_sec0 );
-      sjcd_playing.start.frame = bin2bcd( sjcd_msf.cdmsf_frame0 );
-      sjcd_playing.end.min = bin2bcd( sjcd_msf.cdmsf_min1 );
-      sjcd_playing.end.sec = bin2bcd( sjcd_msf.cdmsf_sec1 );
-      sjcd_playing.end.frame = bin2bcd( sjcd_msf.cdmsf_frame1 );
-
-      if( sjcd_play( &sjcd_playing ) < 0 ){
-	sjcd_audio_status = CDROM_AUDIO_ERROR;
-	return( -EIO );
-      } else sjcd_audio_status = CDROM_AUDIO_PLAY;
-    }
-    return( s );
-  }
-
-  case CDROMREADTOCHDR:{
-    struct cdrom_tochdr toc_header; int s;
-#if defined (SJCD_TRACE )
-    printk( "sjcd: ioctl: readtocheader\n" );
-#endif
-    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( toc_header ) ) ) == 0 ){
-      toc_header.cdth_trk0 = sjcd_first_track_no;
-      toc_header.cdth_trk1 = sjcd_last_track_no;
-      memcpy_tofs( (void *)arg, &toc_header, sizeof( toc_header ) );
-    }
-    return( s );
-  }
-
-  case CDROMREADTOCENTRY:{
-    struct cdrom_tocentry toc_entry; int s;
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: readtocentry\n" );
-#endif
-    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( toc_entry ) ) ) == 0 ){
-      struct sjcd_hw_disk_info *tp;
-
-      memcpy_fromfs( &toc_entry, (void *)arg, sizeof( toc_entry ) );
-
-      if( toc_entry.cdte_track == CDROM_LEADOUT )
-	tp = &sjcd_table_of_contents[ 0 ];
-      else if( toc_entry.cdte_track < sjcd_first_track_no ) return( -EINVAL );
-      else if( toc_entry.cdte_track > sjcd_last_track_no ) return( -EINVAL );
-      else tp = &sjcd_table_of_contents[ toc_entry.cdte_track ];
-
-      toc_entry.cdte_adr = tp->track_control & 0x0F;
-      toc_entry.cdte_ctrl = tp->track_control >> 4;
-
-      switch( toc_entry.cdte_format ){
-      case CDROM_LBA:
-	toc_entry.cdte_addr.lba = msf2hsg( &( tp->un.track_msf ) );
-	break;
-      case CDROM_MSF:
-	toc_entry.cdte_addr.msf.minute = bcd2bin( tp->un.track_msf.min );
-	toc_entry.cdte_addr.msf.second = bcd2bin( tp->un.track_msf.sec );
-	toc_entry.cdte_addr.msf.frame = bcd2bin( tp->un.track_msf.frame );
-	break;
-      default: return( -EINVAL );
-      }
-      memcpy_tofs( (void *)arg, &toc_entry, sizeof( toc_entry ) );
-    }
-    return( s );
-  }
-
-  case CDROMSUBCHNL:{
-    struct cdrom_subchnl subchnl; int s;
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: subchnl\n" );
-#endif
-    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( subchnl ) ) ) == 0 ){
-      struct sjcd_hw_qinfo q_info;
-
-      memcpy_fromfs( &subchnl, (void *)arg, sizeof( subchnl ) );
-      if( sjcd_get_q_info( &q_info ) < 0 ) return( -EIO );
-
-      subchnl.cdsc_audiostatus = sjcd_audio_status;
-      subchnl.cdsc_adr = q_info.track_control & 0x0F;
-      subchnl.cdsc_ctrl = q_info.track_control >> 4;
-      subchnl.cdsc_trk = bcd2bin( q_info.track_no );
-      subchnl.cdsc_ind = bcd2bin( q_info.x );
-
-      switch( subchnl.cdsc_format ){
-      case CDROM_LBA:
-	subchnl.cdsc_absaddr.lba = msf2hsg( &( q_info.abs ) );
-	subchnl.cdsc_reladdr.lba = msf2hsg( &( q_info.rel ) );
-	break;
-      case CDROM_MSF:
-	subchnl.cdsc_absaddr.msf.minute = bcd2bin( q_info.abs.min );
-	subchnl.cdsc_absaddr.msf.second = bcd2bin( q_info.abs.sec );
-	subchnl.cdsc_absaddr.msf.frame = bcd2bin( q_info.abs.frame );
-	subchnl.cdsc_reladdr.msf.minute = bcd2bin( q_info.rel.min );
-	subchnl.cdsc_reladdr.msf.second = bcd2bin( q_info.rel.sec );
-	subchnl.cdsc_reladdr.msf.frame = bcd2bin( q_info.rel.frame );
-	break;
-      default: return( -EINVAL );
-      }
-      memcpy_tofs( (void *)arg, &subchnl, sizeof( subchnl ) );
-    }
-    return( s );
-  }
-
-  case CDROMVOLCTRL:{
-    struct cdrom_volctrl vol_ctrl; int s;
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: volctrl\n" );
-#endif
-    if( ( s = verify_area( VERIFY_READ, (void *)arg, sizeof( vol_ctrl ) ) ) == 0 ){
-      unsigned char dummy[ 4 ];
-
-      memcpy_fromfs( &vol_ctrl, (void *)arg, sizeof( vol_ctrl ) );
-      sjcd_send_4_cmd( SCMD_SET_VOLUME, vol_ctrl.channel0, 0xFF,
-		      vol_ctrl.channel1, 0xFF );
-      if( sjcd_receive_status() < 0 ) return( -EIO );
-      ( void )sjcd_load_response( dummy, 4 );
-    }
-    return( s );
-  }
-
-  case CDROMEJECT:{
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: eject\n" );
-#endif
-    if( !sjcd_command_is_in_progress ){
-      sjcd_tray_unlock();
-      sjcd_send_cmd( SCMD_EJECT_TRAY );
-      ( void )sjcd_receive_status();
-    }
-    return( 0 );
-  }
-
-#if defined( SJCD_GATHER_STAT )
-  case 0xABCD:{
-    int s;
-#if defined( SJCD_TRACE )
-    printk( "sjcd: ioctl: statistic\n" );
-#endif
-    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( statistic ) ) ) == 0 )
-      memcpy_tofs( (void *)arg, &statistic, sizeof( statistic ) );
-    return( s );
-  }
-#endif
-
-  default:
-    return( -EINVAL );
-  }
-}
-
-/*
- * Invalidate internal buffers of the driver.
- */
-static void sjcd_invalidate_buffers( void ){
-  int i;
-  for( i = 0; i < SJCD_BUF_SIZ; sjcd_buf_bn[ i++ ] = -1 );
-  sjcd_buf_out = -1;
-}
-
-/*
- * Take care of the different block sizes between cdrom and Linux.
- * When Linux gets variable block sizes this will probably go away.
- */
-
-#define CURRENT_IS_VALID                                      \
-    ( CURRENT != NULL && MAJOR( CURRENT->rq_dev ) == MAJOR_NR && \
-      CURRENT->cmd == READ && CURRENT->sector != -1 )
-
-static void sjcd_transfer( void ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: transfer:\n" );
-#endif
-  if( CURRENT_IS_VALID ){
-    while( CURRENT->nr_sectors ){
-      int i, bn = CURRENT->sector / 4;
-      for( i = 0; i < SJCD_BUF_SIZ && sjcd_buf_bn[ i ] != bn; i++ );
-      if( i < SJCD_BUF_SIZ ){
-	int offs = ( i * 4 + ( CURRENT->sector & 3 ) ) * 512;
-	int nr_sectors = 4 - ( CURRENT->sector & 3 );
-	if( sjcd_buf_out != i ){
-	  sjcd_buf_out = i;
-	  if( sjcd_buf_bn[ i ] != bn ){
-	    sjcd_buf_out = -1;
-	    continue;
-	  }
-	}
-	if( nr_sectors > CURRENT->nr_sectors )
-	  nr_sectors = CURRENT->nr_sectors;
-#if defined( SJCD_TRACE )
-	printk( "copy out\n" );
-#endif
-	memcpy( CURRENT->buffer, sjcd_buf + offs, nr_sectors * 512 );
-	CURRENT->nr_sectors -= nr_sectors;
-	CURRENT->sector += nr_sectors;
-	CURRENT->buffer += nr_sectors * 512;
-      } else {
-	sjcd_buf_out = -1;
-	break;
-      }
-    }
-  }
-#if defined( SJCD_TRACE )
-  printk( "sjcd: transfer: done\n" );
-#endif
-}
-
-static void sjcd_poll( void ){
-#if defined( SJCD_GATHER_STAT )
-  /*
-   * Update total number of ticks.
-   */
-  statistic.ticks++;
-  statistic.tticks[ sjcd_transfer_state ]++;
-#endif
-
- ReSwitch: switch( sjcd_transfer_state ){
-      
-  case SJCD_S_IDLE:{
-#if defined( SJCD_GATHER_STAT )
-    statistic.idle_ticks++;
-#endif
-#if defined( SJCD_TRACE )
-    printk( "SJCD_S_IDLE\n" );
-#endif
-    return;
-  }
-
-  case SJCD_S_START:{
-#if defined( SJCD_GATHER_STAT )
-    statistic.start_ticks++;
-#endif
-    sjcd_send_cmd( SCMD_GET_STATUS );
-    sjcd_transfer_state =
-      sjcd_mode == SCMD_MODE_COOKED ? SJCD_S_READ : SJCD_S_MODE;
-    sjcd_transfer_timeout = 500;
-#if defined( SJCD_TRACE )
-    printk( "SJCD_S_START: goto SJCD_S_%s mode\n",
-	   sjcd_transfer_state == SJCD_S_READ ? "READ" : "MODE" );
-#endif
-    break;
-  }
-    
-  case SJCD_S_MODE:{
-    if( sjcd_check_status() ){
-      /*
-       * Previous command is completed.
-       */
-      if( !sjcd_status_valid || sjcd_command_failed ){
-#if defined( SJCD_TRACE )
-	printk( "SJCD_S_MODE: pre-cmd failed: goto to SJCD_S_STOP mode\n" );
-#endif
-	sjcd_transfer_state = SJCD_S_STOP;
-	goto ReSwitch;
-      }
-
-      sjcd_mode = 0; /* unknown mode; should not be valid when failed */
-      sjcd_send_1_cmd( SCMD_SET_MODE, SCMD_MODE_COOKED );
-      sjcd_transfer_state = SJCD_S_READ; sjcd_transfer_timeout = 1000;
-#if defined( SJCD_TRACE )
-      printk( "SJCD_S_MODE: goto SJCD_S_READ mode\n" );
-#endif
-    }
-#if defined( SJCD_GATHER_STAT )
-    else statistic.mode_ticks++;
-#endif
-    break;
-  }
-
-  case SJCD_S_READ:{
-    if( sjcd_status_valid ? 1 : sjcd_check_status() ){
-      /*
-       * Previos command is completed.
-       */
-      if( !sjcd_status_valid || sjcd_command_failed ){
-#if defined( SJCD_TRACE )
-	printk( "SJCD_S_READ: pre-cmd failed: goto to SJCD_S_STOP mode\n" );
-#endif
-	sjcd_transfer_state = SJCD_S_STOP;
-	goto ReSwitch;
-      }
-      if( !sjcd_media_is_available ){
-#if defined( SJCD_TRACE )
-	printk( "SJCD_S_READ: no disk: goto to SJCD_S_STOP mode\n" );
-#endif
-	sjcd_transfer_state = SJCD_S_STOP;
-	goto ReSwitch;
-      }
-      if( sjcd_mode != SCMD_MODE_COOKED ){
-	/*
-	 * We seem to come from set mode. So discard one byte of result.
-	 */
-	if( sjcd_load_response( &sjcd_mode, 1 ) != 0 ){
-#if defined( SJCD_TRACE )
-	  printk( "SJCD_S_READ: load failed: goto to SJCD_S_STOP mode\n" );
-#endif
-	  sjcd_transfer_state = SJCD_S_STOP;
-	  goto ReSwitch;
-	}
-	if( sjcd_mode != SCMD_MODE_COOKED ){
-#if defined( SJCD_TRACE )
-	  printk( "SJCD_S_READ: mode failed: goto to SJCD_S_STOP mode\n" );
-#endif
-	  sjcd_transfer_state = SJCD_S_STOP;
-	  goto ReSwitch;
-	}
-      }
-
-      if( CURRENT_IS_VALID ){
-	struct sjcd_play_msf msf;
-
-	sjcd_next_bn = CURRENT->sector / 4;
-	hsg2msf( sjcd_next_bn, &msf.start );
-	msf.end.min = 0; msf.end.sec = 0;            
-	msf.end.frame = sjcd_read_count = SJCD_BUF_SIZ;
-#if defined( SJCD_TRACE )
-	printk( "---reading msf-address %x:%x:%x  %x:%x:%x\n",
-	       msf.start.min, msf.start.sec, msf.start.frame,
-	       msf.end.min,   msf.end.sec,   msf.end.frame );
-	printk( "sjcd_next_bn:%x buf_in:%x buf_out:%x buf_bn:%x\n", \
-	     sjcd_next_bn, sjcd_buf_in, sjcd_buf_out,
-	     sjcd_buf_bn[ sjcd_buf_in ] );
-#endif	
-	sjcd_send_6_cmd( SCMD_DATA_READ, &msf );
-	sjcd_transfer_state = SJCD_S_DATA;
-	sjcd_transfer_timeout = 500;
-#if defined( SJCD_TRACE )
-	printk( "SJCD_S_READ: go to SJCD_S_DATA mode\n" );
-#endif
-      } else {
-#if defined( SJCD_TRACE )
-	printk( "SJCD_S_READ: nothing to read: go to SJCD_S_STOP mode\n" );
-#endif
-	sjcd_transfer_state = SJCD_S_STOP;
-	goto ReSwitch;
-      }
-    }
-#if defined( SJCD_GATHER_STAT )
-    else statistic.read_ticks++;
-#endif
-    break;
-  }
-
-  case SJCD_S_DATA:{
-    unsigned char stat;
-
-  sjcd_s_data: stat = inb( SJCDPORT( 1 ) );
-#if defined( SJCD_TRACE )
-    printk( "SJCD_S_DATA: status = 0x%02x\n", stat );
-#endif
-    if( SJCD_STATUS_AVAILABLE( stat ) ){
-      /*
-       * No data is waiting for us in the drive buffer. Status of operation
-       * completion is available. Read and parse it.
-       */
-      sjcd_load_status();
-
-      if( !sjcd_status_valid || sjcd_command_failed ){
-#if defined( SJCD_TRACE )
-	printk( "sjcd: read block %d failed, maybe audio disk? Giving up\n",
-	       sjcd_next_bn );
-#endif
-	if( CURRENT_IS_VALID ) end_request( 0 );
-#if defined( SJCD_TRACE )
-	printk( "SJCD_S_DATA: pre-cmd failed: go to SJCD_S_STOP mode\n" );
-#endif
-	sjcd_transfer_state = SJCD_S_STOP;
-	goto ReSwitch;
-      }
-
-      if( !sjcd_media_is_available ){
-	printk( "SJCD_S_DATA: no disk: go to SJCD_S_STOP mode\n" );
-	sjcd_transfer_state = SJCD_S_STOP;
-	goto ReSwitch;
-      }
-
-      sjcd_transfer_state = SJCD_S_READ;
-      goto ReSwitch;
-    } else if( SJCD_DATA_AVAILABLE( stat ) ){
-      /*
-       * One frame is read into device buffer. We must copy it to our memory.
-       * Otherwise cdrom hangs up. Check to see if we have something to copy
-       * to.
-       */
-      if( !CURRENT_IS_VALID && sjcd_buf_in == sjcd_buf_out ){
-#if defined( SJCD_TRACE )
-	printk( "SJCD_S_DATA: nothing to read: go to SJCD_S_STOP mode\n" );
-	printk( " ... all the date would be discarded\n" );
-#endif
-	sjcd_transfer_state = SJCD_S_STOP;
-	goto ReSwitch;
-      }
-
-      /*
-       * Everything seems to be OK. Just read the frame and recalculate
-       * indecis.
-       */
-      sjcd_buf_bn[ sjcd_buf_in ] = -1; /* ??? */
-      insb( SJCDPORT( 2 ), sjcd_buf + 2048 * sjcd_buf_in, 2048 );
-#if defined( SJCD_TRACE )
-      printk( "SJCD_S_DATA: next_bn=%d, buf_in=%d, buf_out=%d, buf_bn=%d\n",
-	     sjcd_next_bn, sjcd_buf_in, sjcd_buf_out,
-	     sjcd_buf_bn[ sjcd_buf_in ] );
-#endif
-      sjcd_buf_bn[ sjcd_buf_in ] = sjcd_next_bn++;
-      if( sjcd_buf_out == -1 ) sjcd_buf_out = sjcd_buf_in;
-      if( ++sjcd_buf_in == SJCD_BUF_SIZ ) sjcd_buf_in = 0;
-
-      /*
-       * Only one frame is ready at time. So we should turn over to wait for
-       * another frame. If we need that, of course.
-       */
-      if( --sjcd_read_count == 0 ){
-	/*
-	 * OK, request seems to be precessed. Continue transferring...
-	 */
-	if( !sjcd_transfer_is_active ){
-	  while( CURRENT_IS_VALID ){
-	    /*
-	     * Continue transferring.
-	     */
-	    sjcd_transfer();
-	    if( CURRENT->nr_sectors == 0 ) end_request( 1 );
-	    else break;
-	  }
-	}
-	if( CURRENT_IS_VALID &&
-	   ( CURRENT->sector / 4 < sjcd_next_bn ||
-	    CURRENT->sector / 4 > sjcd_next_bn + SJCD_BUF_SIZ ) ){
-#if defined( SJCD_TRACE )
-	  printk( "SJCD_S_DATA: can't read: go to SJCD_S_STOP mode\n" );
-#endif
-	  sjcd_transfer_state = SJCD_S_STOP;
-	  goto ReSwitch;
-	}
-      }
-      /*
-       * Now we should turn around rather than wait for while.
-       */
-      goto sjcd_s_data;
-    }
-#if defined( SJCD_GATHER_STAT )
-    else statistic.data_ticks++;
-#endif
-    break;
-  }
-
-  case SJCD_S_STOP:{
-    sjcd_read_count = 0;
-    sjcd_send_cmd( SCMD_STOP );
-    sjcd_transfer_state = SJCD_S_STOPPING;
-    sjcd_transfer_timeout = 500;
-#if defined( SJCD_GATHER_STAT )
-    statistic.stop_ticks++;
-#endif
-    break;
-  }
-
-  case SJCD_S_STOPPING:{
-    unsigned char stat;
-    
-    stat = inb( SJCDPORT( 1 ) );
-#if defined( SJCD_TRACE )
-    printk( "SJCD_S_STOP: status = 0x%02x\n", stat );
-#endif      
-    if( SJCD_DATA_AVAILABLE( stat ) ){
-      int i;
-#if defined( SJCD_TRACE )
-      printk( "SJCD_S_STOP: discard data\n" );
-#endif
-      /*
-       * Discard all the data from the pipe. Foolish method.
-       */
-      for( i = 2048; i--; ( void )inb( SJCDPORT( 2 ) ) );
-      sjcd_transfer_timeout = 500;
-    } else if( SJCD_STATUS_AVAILABLE( stat ) ){
-      sjcd_load_status();
-      if( sjcd_status_valid && sjcd_media_is_changed ) {
-	sjcd_toc_uptodate = 0;
-	sjcd_invalidate_buffers();
-      }
-      if( CURRENT_IS_VALID ){
-	if( sjcd_status_valid ) sjcd_transfer_state = SJCD_S_READ;
-	else sjcd_transfer_state = SJCD_S_START;
-      } else sjcd_transfer_state = SJCD_S_IDLE;
-      goto ReSwitch;
-    }
-#if defined( SJCD_GATHER_STAT )
-    else statistic.stopping_ticks++;
-#endif
-    break;
-  }
-
-  default:
-    printk( "sjcd_poll: invalid state %d\n", sjcd_transfer_state );
-    return;
-  }
-  
-  if( --sjcd_transfer_timeout == 0 ){
-    printk( "sjcd: timeout in state %d\n", sjcd_transfer_state );
-    while( CURRENT_IS_VALID ) end_request( 0 );
-    sjcd_send_cmd( SCMD_STOP );
-    sjcd_transfer_state = SJCD_S_IDLE;
-    goto ReSwitch;
-    }
-
-  /*
-   * Get back in some time. 1 should be replaced with count variable to
-   * avoid unnecessary testings.
-   */
-  SJCD_SET_TIMER( sjcd_poll, 1 );
-}
-
-static void do_sjcd_request( void ){
-#if defined( SJCD_TRACE )
-  printk( "sjcd: do_sjcd_request(%ld+%ld)\n",
-	 CURRENT->sector, CURRENT->nr_sectors );
-#endif
-  sjcd_transfer_is_active = 1;
-  while( CURRENT_IS_VALID ){
-    /*
-     * Who of us are paranoic?
-     */
-    if( CURRENT->bh && !( CURRENT->bh->b_lock ) )
-      panic( DEVICE_NAME ": block not locked" );
-
-    sjcd_transfer();
-    if( CURRENT->nr_sectors == 0 ) end_request( 1 );
-    else {
-      sjcd_buf_out = -1;         /* Want to read a block not in buffer */
-      if( sjcd_transfer_state == SJCD_S_IDLE ){
-	if( !sjcd_toc_uptodate ){
-	  if( sjcd_update_toc() < 0 ){
-	    printk( "sjcd: transfer: discard\n" );
-	    while( CURRENT_IS_VALID ) end_request( 0 );
-	    break;
-	  }
-	}
-	sjcd_transfer_state = SJCD_S_START;
-	SJCD_SET_TIMER( sjcd_poll, HZ/100 );
-      }
-      break;
-    }
-  }
-  sjcd_transfer_is_active = 0;
-#if defined( SJCD_TRACE )
-  printk( "sjcd_next_bn:%x sjcd_buf_in:%x sjcd_buf_out:%x sjcd_buf_bn:%x\n",
-	 sjcd_next_bn, sjcd_buf_in, sjcd_buf_out, sjcd_buf_bn[ sjcd_buf_in ] );
-  printk( "do_sjcd_request ends\n" );
-#endif
-}
-
-/*
- * Open the device special file. Check disk is in.
- */
-int sjcd_open( struct inode *ip, struct file *fp ){
-  /*
-   * Check the presence of device.
-   */
-  if( !sjcd_present ) return( -ENXIO );
-  
-  /*
-   * Only read operations are allowed. Really? (:-)
-   */
-  if( fp->f_mode & 2 ) return( -EROFS );
-  
-  if( sjcd_open_count == 0 ){
-    int s, sjcd_open_tries;
-/* We don't know that, do we? */
-/*
-    sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
-*/
-    sjcd_mode = 0;
-    sjcd_door_was_open = 0;
-    sjcd_transfer_state = SJCD_S_IDLE;
-    sjcd_invalidate_buffers();
-    sjcd_status_valid = 0;
-
-    /*
-     * Strict status checking.
-     */
-    for( sjcd_open_tries = 4; --sjcd_open_tries; ){
-      if( !sjcd_status_valid ) sjcd_get_status();
-      if( !sjcd_status_valid ){
-#if defined( SJCD_DIAGNOSTIC )
-	printk( "sjcd: open: timed out when check status.\n" );
-#endif
-	return( -EIO );
-      } else if( !sjcd_media_is_available ){
-#if defined( SJCD_DIAGNOSTIC )
-	printk("sjcd: open: no disk in drive\n");
-#endif
-	if( !sjcd_door_closed ){
-	  sjcd_door_was_open = 1;
-#if defined( SJCD_TRACE )
-	  printk("sjcd: open: close the tray\n");
-#endif
-	  s = sjcd_tray_close();
-	  if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
-#if defined( SJCD_DIAGNOSTIC )
-	    printk("sjcd: open: tray close attempt failed\n");
-#endif
-	    return( -EIO );
-	  }
-	  continue;
-	} else return( -EIO );
-      }
-      break;
-    }
-    s = sjcd_tray_lock();
-    if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
-#if defined( SJCD_DIAGNOSTIC )
-      printk("sjcd: open: tray lock attempt failed\n");
-#endif
-      return( -EIO );
-    }
-#if defined( SJCD_TRACE )
-    printk( "sjcd: open: done\n" );
-#endif
-  }
-#ifdef MODULE
-  MOD_INC_USE_COUNT;
-#endif
-  ++sjcd_open_count;
-  return( 0 );
-}
-
-/*
- * On close, we flush all sjcd blocks from the buffer cache.
- */
-static void sjcd_release( struct inode *inode, struct file *file ){
-  int s;
-
-#if defined( SJCD_TRACE )
-  printk( "sjcd: release\n" );
-#endif
-#ifdef MODULE
-  MOD_DEC_USE_COUNT;
-#endif
-  if( --sjcd_open_count == 0 ){
-    sjcd_invalidate_buffers();
-    sync_dev( inode->i_rdev );
-    invalidate_buffers( inode->i_rdev );
-    s = sjcd_tray_unlock();
-    if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
-#if defined( SJCD_DIAGNOSTIC )
-      printk("sjcd: release: tray unlock attempt failed.\n");
-#endif
-    }
-    if( sjcd_door_was_open ){
-      s = sjcd_tray_open();
-      if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
-#if defined( SJCD_DIAGNOSTIC )
-	printk("sjcd: release: tray unload attempt failed.\n");
-#endif
-      }
-    }
-  }
-}
-
-/*
- * A list of file operations allowed for this cdrom.
- */
-static struct file_operations sjcd_fops = {
-  NULL,               /* lseek - default */
-  block_read,         /* read - general block-dev read */
-  block_write,        /* write - general block-dev write */
-  NULL,               /* readdir - bad */
-  NULL,               /* select */
-  sjcd_ioctl,         /* ioctl */
-  NULL,               /* mmap */
-  sjcd_open,          /* open */
-  sjcd_release,       /* release */
-  NULL,               /* fsync */
-  NULL,               /* fasync */
-  sjcd_disk_change,   /* media change */
-  NULL                /* revalidate */
-};
-
-/*
- * Following stuff is intended for initialization of the cdrom. It
- * first looks for presence of device. If the device is present, it
- * will be reset. Then read the version of the drive and load status.
- * The version is two BCD-coded bytes.
- */
-static struct {
-  unsigned char major, minor;
-} sjcd_version;
-
-/*
- * Test for presence of drive and initialize it. Called at boot time.
- * Probe cdrom, find out version and status.
- */
-int sjcd_init( void ){
-  int i;
-
-  if ( (isp16_type=isp16_detect()) < 0 )
-    printk( "No ISP16 cdrom interface found.\n" );
-  else {
-    u_char expected_drive;
-
-    printk( "ISP16 cdrom interface (with OPTi 82C92%s chip) detected.\n",
-      (isp16_type==2)?"9":"8" );
-
-    printk( "ISP16 sound configuration.\n" );
-    isp16_sound_config();
-
-    expected_drive = (isp16_type?ISP16_SANYO1:ISP16_SANYO0);
-
-    if ( isp16_cdi_config( sjcd_port, expected_drive, sjcd_irq, sjcd_dma ) < 0 ) {
-      printk( "ISP16 cdrom interface has not been properly configured.\n" );
-      return( -EIO );
-    }
-  }
-
-#if defined( SJCD_TRACE )
-  printk( "sjcd=0x%x,%d: ", sjcd_port, sjcd_irq );
-#endif  
-
-  if( register_blkdev( MAJOR_NR, "sjcd", &sjcd_fops ) != 0 ){
-    printk( "Unable to get major %d for Sanyo CD-ROM\n", MAJOR_NR );
-    return( -EIO );
-  }
-  
-  blk_dev[ MAJOR_NR ].request_fn = DEVICE_REQUEST;
-  read_ahead[ MAJOR_NR ] = 4;
-  
-  if( check_region( sjcd_port, 4 ) ){
-    printk( "Init failed, I/O port (%X) is already in use\n",
-      sjcd_port );
-    return( -EIO );
-  }
-  
-  /*
-   * Check for card. Since we are booting now, we can't use standard
-   * wait algorithm.
-   */
-  printk( "Sanyo: Resetting: " );
-  sjcd_send_cmd( SCMD_RESET );
-  for( i = 1000; i-- > 0 && !sjcd_status_valid; ){
-    unsigned long timer;
-
-    /*
-     * Wait 10ms approx.
-     */
-    for( timer = jiffies; jiffies <= timer; );
-    if ( (i % 100) == 0 ) printk( "." );
-    ( void )sjcd_check_status();
-  }
-  if( i == 0 || sjcd_command_failed ){
-    printk( " reset failed, no drive found.\n" );
-    return( -EIO );
-  } else printk( "\n" );
-
-  /*
-   * Get and print out cdrom version.
-   */
-  printk( "Sanyo: Getting version: " );
-  sjcd_send_cmd( SCMD_GET_VERSION );
-  for( i = 1000; i > 0 && !sjcd_status_valid; --i ){
-    unsigned long timer;
-
-    /*
-     * Wait 10ms approx.
-     */
-    for( timer = jiffies; jiffies <= timer; );
-    if ( (i % 100) == 0 ) printk( "." );
-    ( void )sjcd_check_status();
-  }
-  if( i == 0 || sjcd_command_failed ){
-    printk( " get version failed, no drive found.\n" );
-    return( -EIO );
-  }
-
-  if( sjcd_load_response( &sjcd_version, sizeof( sjcd_version ) ) == 0 ){
-    printk( " %1x.%02x\n", ( int )sjcd_version.major,
-	     ( int )sjcd_version.minor );
-  } else {
-    printk( " read version failed, no drive found.\n" );
-    return( -EIO );
-  }
-
-  /*
-   * Check and print out the tray state. (if it is needed?).
-   */
-  if( !sjcd_status_valid ){
-    printk( "Sanyo: Getting status: " );
-    sjcd_send_cmd( SCMD_GET_STATUS );
-    for( i = 1000; i > 0 && !sjcd_status_valid; --i ){
-      unsigned long timer;
-
-      /*
-       * Wait 10ms approx.
-       */
-      for( timer = jiffies; jiffies <= timer; );
-      if ( (i % 100) == 0 ) printk( "." );
-      ( void )sjcd_check_status();
-    }
-    if( i == 0 || sjcd_command_failed ){
-      printk( " get status failed, no drive found.\n" );
-      return( -EIO );
-    } else printk( "\n" );
-  }
-
-  printk( "SANYO CDR-H94A: Status: port=0x%x, irq=%d, dma=%d.\n",
-	 sjcd_port, sjcd_irq, sjcd_dma );
-
-  sjcd_present++;
-  return( 0 );
-}
-
-#ifdef MODULE
-void cleanup_module( void ){
-  if( MOD_IN_USE ){
-    printk( "sjcd: module: in use - can not remove.\n" );
-  } else if( ( unregister_blkdev( MAJOR_NR, "sjcd" ) == -EINVAL ) ){
-    printk( "sjcd: module: can not unregister device.\n" );
-  } else {
-    release_region( sjcd_port, 4 );
-    printk( "sjcd: module: removed.\n");
-  }
-}
-#endif
-
-/*
- * -- ISP16 detection and configuration
- *
- *    Copyright (c) 1995, Eric van der Maarel <maarel@marin.nl>
- *
- *    Version 0.5
- *
- *    Detect cdrom interface on ISP16 soundcard.
- *    Configure cdrom interface.
- *    Configure sound interface.
- *
- *    Algorithm for the card with OPTi 82C928 taken
- *    from the CDSETUP.SYS driver for MSDOS,
- *    by OPTi Computers, version 2.03.
- *    Algorithm for the card with OPTi 82C929 as communicated
- *    to me by Vadim Model and Leo Spiekman.
- *
- *    Use, modifification or redistribution of this software is
- *    allowed under the terms of the GPL.
- *
- */
-
-
-#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p))
-#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p))
-
-static short
-isp16_detect(void)
-{
-
-  if ( !( isp16_c929__detect() < 0 ) )
-    return(2);
-  else
-    return( isp16_c928__detect() );
-}
-
-static short
-isp16_c928__detect(void)
-{
-  u_char ctrl;
-  u_char enable_cdrom;
-  u_char io;
-  short i = -1;
-
-  isp16_ctrl = ISP16_C928__CTRL;
-  isp16_enable_port = ISP16_C928__ENABLE_PORT;
-
-  /* read' and write' are a special read and write, respectively */
-
-  /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */
-  ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC;
-  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-
-  /* read' 3,4 and 5-bit from the cdrom enable port */
-  enable_cdrom = ISP16_IN( ISP16_C928__ENABLE_PORT ) & 0x38;
-
-  if ( !(enable_cdrom & 0x20) ) {  /* 5-bit not set */
-    /* read' last 2 bits of ISP16_IO_SET_PORT */
-    io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03;
-    if ( ((io&0x01)<<1) == (io&0x02) ) {  /* bits are the same */
-      if ( io == 0 ) {  /* ...the same and 0 */
-        i = 0;
-        enable_cdrom |= 0x20;
-      }
-      else {  /* ...the same and 1 */  /* my card, first time 'round */
-        i = 1;
-        enable_cdrom |= 0x28;
-      }
-      ISP16_OUT( ISP16_C928__ENABLE_PORT, enable_cdrom );
-    }
-    else {  /* bits are not the same */
-      ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-      return(i); /* -> not detected: possibly incorrect conclusion */
-    }
-  }
-  else if ( enable_cdrom == 0x20 )
-    i = 0;
-  else if ( enable_cdrom == 0x28 )  /* my card, already initialised */
-    i = 1;
-
-  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-
-  return(i);
-}
-
-static short
-isp16_c929__detect(void)
-{
-  u_char ctrl;
-  u_char tmp;
-
-  isp16_ctrl = ISP16_C929__CTRL;
-  isp16_enable_port = ISP16_C929__ENABLE_PORT;
-
-  /* read' and write' are a special read and write, respectively */
-
-  /* read' ISP16_CTRL_PORT and save */
-  ctrl = ISP16_IN( ISP16_CTRL_PORT );
-
-  /* write' zero to the ctrl port and get response */
-  ISP16_OUT( ISP16_CTRL_PORT, 0 );
-  tmp = ISP16_IN( ISP16_CTRL_PORT );
-
-  if ( tmp != 2 )  /* isp16 with 82C929 not detected */
-    return(-1);
-
-  /* restore ctrl port value */
-  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
-  
-  return(2);
-}
-
-static short
-isp16_cdi_config( int base, u_char drive_type, int irq, int dma )
-{
-  u_char base_code;
-  u_char irq_code;
-  u_char dma_code;
-  u_char i;
-
-  if ( (drive_type == ISP16_MITSUMI) && (dma != 0) )
-    printk( "Mitsumi cdrom drive has no dma support.\n" );
-
-  switch (base) {
-  case 0x340: base_code = ISP16_BASE_340; break;
-  case 0x330: base_code = ISP16_BASE_330; break;
-  case 0x360: base_code = ISP16_BASE_360; break;
-  case 0x320: base_code = ISP16_BASE_320; break;
-  default:
-    printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base );
-    return(-1);
-  }
-  switch (irq) {
-  case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */
-  case 5: irq_code = ISP16_IRQ_5;
-          printk( "Irq 5 shouldn't be used by cdrom interface on ISP16,"
-            " due to possible conflicts with the soundcard.\n");
-          break;
-  case 7: irq_code = ISP16_IRQ_7;
-          printk( "Irq 7 shouldn't be used by cdrom interface on ISP16,"
-            " due to possible conflicts with the soundcard.\n");
-          break;
-  case 3: irq_code = ISP16_IRQ_3; break;
-  case 9: irq_code = ISP16_IRQ_9; break;
-  case 10: irq_code = ISP16_IRQ_10; break;
-  case 11: irq_code = ISP16_IRQ_11; break;
-  default:
-    printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq );
-    return(-1);
-  }
-  switch (dma) {
-  case 0: dma_code = ISP16_DMA_X; break;  /* disable dma */
-  case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16,"
-            " due to conflict with the soundcard.\n");
-          return(-1); break;
-  case 3: dma_code = ISP16_DMA_3; break;
-  case 5: dma_code = ISP16_DMA_5; break;
-  case 6: dma_code = ISP16_DMA_6; break;
-  case 7: dma_code = ISP16_DMA_7; break;
-  default:
-    printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma );
-    return(-1);
-  }
-
-  if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 &&
-    drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 &&
-    drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI &&
-    drive_type != ISP16_DRIVE_X ) {
-    printk( "Drive type (code 0x%02X) not supported by cdrom"
-     " interface on ISP16.\n", drive_type );
-    return(-1);
-  }
-
-  /* set type of interface */
-  i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK;  /* clear some bits */
-  ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type );
-
-  /* enable cdrom on interface with 82C929 chip */
-  if ( isp16_type > 1 )
-    ISP16_OUT( isp16_enable_port, ISP16_ENABLE_CDROM );
-
-  /* set base address, irq and dma */
-  i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK;  /* keep some bits */
-  ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code );
-
-  return(0);
-}
-
-static void isp16_sound_config( void )
-{
-  int i;
-  u_char saved;
-
-  saved = ISP16_IN( 0xF8D ) & 0x8F;
-    
-  ISP16_OUT( 0xF8D, 0x40 );
-
-  /*
-   * Now we should wait for a while...
-   */
-  for( i = 16*1024; i--; );
-  
-  ISP16_OUT( 0xF8D, saved );
-
-  ISP16_OUT( 0xF91, 0x1B );
-
-  for( i = 5*64*1024; i != 0; i-- )
-    if( !( inb( 0x534 ) & 0x80 ) ) break;
-
-  if( i > 0 ) {
-    saved = ( inb( 0x534 ) & 0xE0 ) | 0x0A;
-    outb( saved, 0x534 );
-
-    special_mask = ( inb( 0x535 ) >> 4 ) & 0x08;
-
-    saved = ( inb( 0x534 ) & 0xE0 ) | 0x0C;
-    outb( saved, 0x534 );
-
-    switch( inb( 0x535 ) ) {
-      case 0x09:
-      case 0x0A:
-        special_mask |= 0x05;
-        break;
-      case 0x8A:
-        special_mask = 0x0F;
-        break;
-      default:
-        i = 0;
-    }
-  }
-  if ( i == 0 ) {
-    printk( "Strange MediaMagic, but\n" );
-  }
-  else {
-    printk( "Conf:" );
-    saved = inb( 0x534 ) & 0xE0;
-    for( i = 0; i < 16; i++ ) {
-      outb( 0x20 | ( u_char )i, 0x534 );
-      outb( defaults[i], 0x535 );
-    }
-    for ( i = 0; i < 16; i++ ) {
-      outb( 0x20 | ( u_char )i, 0x534 );
-      saved = inb( 0x535 );
-      printk( " %02X", saved );
-    }
-    printk( "\n" );
-  }
-
-  ISP16_OUT( 0xF91, 0xA0 | special_mask );
-
-  /*
-   * The following have no explaination yet.
-   */
-  ISP16_OUT( 0xF90, 0xA2 );
-  ISP16_OUT( 0xF92, 0x03 );
-
-  /*
-   * Turn general sound on and set total volume.
-   */
-  ISP16_OUT( 0xF93, 0x0A );
-
-/*
-  outb( 0x04, 0x224 );
-  saved = inb( 0x225 );
-  outb( 0x04, 0x224 );
-  outb( saved, 0x225 );
-*/
-
-}
diff -u --recursive --new-file v1.3.35/linux/drivers/block/sonycd535.c linux/drivers/block/sonycd535.c
--- v1.3.35/linux/drivers/block/sonycd535.c	Tue Oct 10 18:46:32 1995
+++ linux/drivers/block/sonycd535.c	Thu Jan  1 02:00:00 1970
@@ -1,1703 +0,0 @@
-/*
- * Sony CDU-535 interface device driver
- *
- * This is a modified version of the CDU-31A device driver (see below).
- * Changes were made using documentation for the CDU-531 (which Sony
- * assures me is very similar to the 535) and partial disassembly of the
- * DOS driver.  I used Minyard's driver and replaced the the CDU-31A
- * commands with the CDU-531 commands.  This was complicated by a different
- * interface protocol with the drive.  The driver is still polled.
- *
- * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec.
- * I tried polling without the sony_sleep during the data transfers but
- * it did not speed things up any.
- *
- * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict
- * with CDU-31A driver.  This is the also the number from the Linux
- * Device Driver Registry for the Sony Drive.  Hope nobody else is using it.
- *
- * 1993-08-29 (rgj) remove the configuring of the interface board address
- * from the top level configuration, you have to modify it in this file.
- *
- * 1995-01-26 Made module-capable (Joel Katz <Stimpson@Panix.COM>)
- *
- * 1995-05-20
- *  Modified to support CDU-510/515 series
- *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
- *  Fixed to report verify_area() failures
- *      (Heiko Eissfeldt <heiko@colossus.escape.de>)
- *
- * 1995-06-01
- *  More changes to support CDU-510/515 series
- *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
- *
- * Things to do:
- *  - handle errors and status better, put everything into a single word
- *  - use interrupts (code mostly there, but a big hole still missing)
- *  - handle multi-session CDs?
- *  - use DMA?
- *
- *  Known Bugs:
- *  -
- *
- *   Ken Pizzini (ken@halcyon.com)
- *
- * Original by:
- *   Ron Jeppesen (ronj.an@site007.saic.com)
- *
- *
- *------------------------------------------------------------------------
- * Sony CDROM interface device driver.
- *
- * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to Ken above)
- *
- * Colossians 3:17
- *
- * The Sony interface device driver handles Sony interface CDROM
- * drives and provides a complete block-level interface as well as an
- * ioctl() interface compatible with the Sun (as specified in
- * include/linux/cdrom.h).  With this interface, CDROMs can be
- * accessed and standard audio CDs can be played back normally.
- *
- * This interface is (unfortunately) a polled interface.  This is
- * because most Sony interfaces are set up with DMA and interrupts
- * disables.  Some (like mine) do not even have the capability to
- * handle interrupts or DMA.  For this reason you will see a lot of
- * the following:
- *
- *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
- *   while ((retry_count > jiffies) && (! <some condition to wait for))
- *   {
- *      while (handle_sony_cd_attention())
- *         ;
- *
- *      sony_sleep();
- *   }
- *   if (the condition not met)
- *   {
- *      return an error;
- *   }
- *
- * This ugly hack waits for something to happen, sleeping a little
- * between every try.  it also handles attentions, which are
- * asynchronous events from the drive informing the driver that a disk
- * has been inserted, removed, etc.
- *
- * One thing about these drives: They talk in MSF (Minute Second Frame) format.
- * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
- * disk.  The funny thing is that these are sent to the drive in BCD, but the
- * interface wants to see them in decimal.  A lot of conversion goes on.
- *
- *  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.
- *
- */
-
-
-#include <linux/config.h>
-
-#ifdef MODULE
-# include <linux/module.h>
-# include <linux/version.h>
-# ifndef CONFIG_MODVERSIONS
-	char kernel_version[]= UTS_RELEASE;
-# endif
-#define sony535_init init_module
-#else
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
-#endif
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/hdreg.h>
-#include <linux/genhd.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-
-#define REALLY_SLOW_IO
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#include <linux/cdrom.h>
-
-#define MAJOR_NR CDU535_CDROM_MAJOR
-# include "blk.h"
-#define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */
-#include <linux/sonycd535.h>
-
-/*
- * this is the base address of the interface card for the Sony CDU-535
- * CDROM drive.  If your jumpers are set for an address other than
- * this one (the default), change the following line to the
- * proper address.
- */
-#ifndef CDU535_ADDRESS
-# define CDU535_ADDRESS			0x340
-#endif
-#ifndef CDU535_INTERRUPT
-# define CDU535_INTERRUPT		0
-#endif
-#ifndef CDU535_HANDLE
-# define CDU535_HANDLE			"cdu535"
-#endif
-#ifndef CDU535_MESSAGE_NAME
-# define CDU535_MESSAGE_NAME	"Sony CDU-535"
-#endif
-
-#ifndef MAX_SPINUP_RETRY
-# define MAX_SPINUP_RETRY		3	/* 1 is sufficient for most drives... */
-#endif
-#ifndef RETRY_FOR_BAD_STATUS
-# define RETRY_FOR_BAD_STATUS	100	/* in 10th of second */
-#endif
-
-#ifndef DEBUG
-# define DEBUG	1
-#endif
-
-/*
- *  SONY535_BUFFER_SIZE determines the size of internal buffer used
- *  by the drive.  It must be at least 2K and the larger the buffer
- *  the better the transfer rate.  It does however take system memory.
- *  On my system I get the following transfer rates using dd to read
- *  10 Mb off /dev/cdrom.
- *
- *    8K buffer      43 Kb/sec
- *   16K buffer      66 Kb/sec
- *   32K buffer      91 Kb/sec
- *   64K buffer     111 Kb/sec
- *  128K buffer     123 Kb/sec
- *  512K buffer     123 Kb/sec
- */
-#define SONY535_BUFFER_SIZE	(64*1024)
-
-/*
- *  if LOCK_DOORS is defined then the eject button is disabled while
- * the device is open.
- */
-#ifndef NO_LOCK_DOORS
-# define LOCK_DOORS
-#endif
-
-static int read_subcode(void);
-static void sony_get_toc(void);
-static int cdu_open(struct inode *inode, struct file *filp);
-static inline unsigned int int_to_bcd(unsigned int val);
-static unsigned int bcd_to_int(unsigned int bcd);
-static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2],
-					   Byte * response, int n_response, int ignoreStatusBit7);
-
-/* The base I/O address of the Sony Interface.  This is a variable (not a
-   #define) so it can be easily changed via some future ioctl() */
-#ifndef MODULE
-static
-#endif
-unsigned short sony535_cd_base_io = CDU535_ADDRESS;
-
-/*
- * The following are I/O addresses of the various registers for the drive.  The
- * comment for the base address also applies here.
- */
-static unsigned short select_unit_reg;
-static unsigned short result_reg;
-static unsigned short command_reg;
-static unsigned short read_status_reg;
-static unsigned short data_reg;
-
-static int initialized = 0;			/* Has the drive been initialized? */
-static int sony_disc_changed = 1;	/* Has the disk been changed
-					   since the last check? */
-static int sony_toc_read = 0;		/* Has the table of contents been
-					   read? */
-static unsigned int sony_buffer_size;	/* Size in bytes of the read-ahead
-					   buffer. */
-static unsigned int sony_buffer_sectors;	/* Size (in 2048 byte records) of
-						   the read-ahead buffer. */
-static unsigned int sony_usage = 0;	/* How many processes have the
-					   drive open. */
-
-static int sony_first_block = -1;	/* First OS block (512 byte) in
-					   the read-ahead buffer */
-static int sony_last_block = -1;	/* Last OS block (512 byte) in
-					   the read-ahead buffer */
-
-static struct s535_sony_toc *sony_toc;	/* Points to the table of
-					   contents. */
-
-static struct s535_sony_subcode *last_sony_subcode;		/* Points to the last
-								   subcode address read */
-static Byte **sony_buffer;		/* Points to the pointers
-					   to the sector buffers */
-
-static int sony_inuse = 0;		/* is the drive in use? Only one
-					   open at a time allowed */
-
-/*
- * The audio status uses the values from read subchannel data as specified
- * in include/linux/cdrom.h.
- */
-static int sony_audio_status = CDROM_AUDIO_NO_STATUS;
-
-/*
- * The following are a hack for pausing and resuming audio play.  The drive
- * does not work as I would expect it, if you stop it then start it again,
- * the drive seeks back to the beginning and starts over.  This holds the
- * position during a pause so a resume can restart it.  It uses the
- * audio status variable above to tell if it is paused.
- *   I just kept the CDU-31A driver behavior rather than using the PAUSE
- * command on the CDU-535.
- */
-static Byte cur_pos_msf[3] = {0, 0, 0};
-static Byte final_pos_msf[3] = {0, 0, 0};
-
-/* What IRQ is the drive using?  0 if none. */
-#ifndef MODULE
-static
-#endif
-int sony535_irq_used = CDU535_INTERRUPT;
-
-/* The interrupt handler will wake this queue up when it gets an interrupt. */
-static struct wait_queue *cdu535_irq_wait = NULL;
-
-
-/*
- * This routine returns 1 if the disk has been changed since the last
- * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
- */
-static int
-cdu535_check_media_change(kdev_t full_dev)
-{
-	int retval;
-
-	if (MINOR(full_dev) != 0) {
-		printk(CDU535_MESSAGE_NAME " request error: invalid device.\n");
-		return 0;
-	}
-
-	/* if driver is not initialized, always return 0 */
-	retval = initialized ? sony_disc_changed : 0;
-	sony_disc_changed = 0;
-	return retval;
-}
-
-static inline void
-enable_interrupts(void)
-{
-#ifdef USE_IRQ
-	/* this code snarfed from cdu31a.c; it will not
-	 * directly work for the cdu535 as written...
-	 */
-	curr_control_reg |= ( SONY_ATTN_INT_EN_BIT
-						| SONY_RES_RDY_INT_EN_BIT
-						| SONY_DATA_RDY_INT_EN_BIT);
-	outb(curr_control_reg, sony_cd_control_reg);
-#endif
-}
-
-static inline void
-disable_interrupts(void)
-{
-#ifdef USE_IRQ
-	/* this code snarfed from cdu31a.c; it will not
-	 * directly work for the cdu535 as written...
-	 */
-	curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT
-						| SONY_RES_RDY_INT_EN_BIT
-						| SONY_DATA_RDY_INT_EN_BIT);
-	outb(curr_control_reg, sony_cd_control_reg);
-#endif
-}
-
-static void
-cdu535_interrupt(int irq, struct pt_regs *regs)
-{
-	disable_interrupts();
-	if (cdu535_irq_wait != NULL)
-		wake_up(&cdu535_irq_wait);
-	else
-		printk(CDU535_MESSAGE_NAME
-				": Got an interrupt but nothing was waiting\n");
-}
-
-
-/*
- * Wait a little while (used for polling the drive).  If in initialization,
- * setting a timeout doesn't work, so just loop for a while.  (We trust
- * that the sony_sleep() call is protected by a test for proper jiffies count.)
- */
-static inline void
-sony_sleep(void)
-{
-	if (sony535_irq_used <= 0) {	/* poll */
-		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = jiffies;
-		schedule();
-	} else {	/* Interrupt driven */
-		cli();
-		enable_interrupts();
-		interruptible_sleep_on(&cdu535_irq_wait);
-		sti();
-	}
-}
-
-/*------------------start of SONY CDU535 very specific ---------------------*/
-
-/****************************************************************************
- * void select_unit( int unit_no )
- *
- *  Select the specified unit (0-3) so that subsequent commands reference it
- ****************************************************************************/
-static void
-select_unit(int unit_no)
-{
-	unsigned int select_mask = ~(1 << unit_no);
-	outb(select_mask, select_unit_reg);
-}
-
-/***************************************************************************
- * int read_result_reg( Byte *data_ptr )
- *
- *  Read a result byte from the Sony CDU controller, store in location pointed
- * to by data_ptr.  Return zero on success, TIME_OUT if we did not receive
- * data.
- ***************************************************************************/
-static int
-read_result_reg(Byte *data_ptr)
-{
-	int retry_count;
-	int read_status;
-
-	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-	while (jiffies < retry_count) {
-		if (((read_status = inb(read_status_reg)) & SONY535_RESULT_NOT_READY_BIT) == 0) {
-#if DEBUG > 1
-			printk(CDU535_MESSAGE_NAME
-					": read_result_reg(): readStatReg = 0x%x\n", read_status);
-#endif
-			*data_ptr = inb(result_reg);
-			return 0;
-		} else {
-			sony_sleep();
-		}
-	}
-	printk(CDU535_MESSAGE_NAME " read_result_reg: TIME OUT!\n");
-	return TIME_OUT;
-}
-
-/****************************************************************************
- * int read_exec_status( Byte status[2] )
- *
- *  Read the execution status of the last command and put into status.
- * Handles reading second status word if available.  Returns 0 on success,
- * TIME_OUT on failure.
- ****************************************************************************/
-static int
-read_exec_status(Byte status[2])
-{
-	status[1] = 0;
-	if (read_result_reg(&(status[0])) != 0)
-		return TIME_OUT;
-	if ((status[0] & 0x80) != 0) {	/* byte two follows */
-		if (read_result_reg(&(status[1])) != 0)
-			return TIME_OUT;
-	}
-#if DEBUG > 1
-	printk(CDU535_MESSAGE_NAME ": read_exec_status: read 0x%x 0x%x\n",
-			status[0], status[1]);
-#endif
-	return 0;
-}
-
-/****************************************************************************
- * int check_drive_status( void )
- *
- *  Check the current drive status.  Using this before executing a command
- * takes care of the problem of unsolicited drive status-2 messages.
- * Add a check of the audio status if we think the disk is playing.
- ****************************************************************************/
-static int
-check_drive_status(void)
-{
-	Byte status, e_status[2];
-	int  CDD, ATN;
-	Byte cmd;
-
-	select_unit(0);
-	if (sony_audio_status == CDROM_AUDIO_PLAY) {	/* check status */
-		outb(SONY535_REQUEST_AUDIO_STATUS, command_reg);
-		if (read_result_reg(&status) == 0) {
-			switch (status) {
-			case 0x0:
-				break;		/* play in progress */
-			case 0x1:
-				break;		/* paused */
-			case 0x3:		/* audio play completed */
-			case 0x5:		/* play not requested */
-				sony_audio_status = CDROM_AUDIO_COMPLETED;
-				read_subcode();
-				break;
-			case 0x4:		/* error during play */
-				sony_audio_status = CDROM_AUDIO_ERROR;
-				break;
-			}
-		}
-	}
-	/* now check drive status */
-	outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg);
-	if (read_result_reg(&status) != 0)
-		return TIME_OUT;
-
-#if DEBUG > 1
-	printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status);
-#endif
-
-	if (status == 0)
-		return 0;
-
-	ATN = status & 0xf;
-	CDD = (status >> 4) & 0xf;
-
-	switch (ATN) {
-	case 0x0:
-		break;					/* go on to CDD stuff */
-	case SONY535_ATN_BUSY:
-		if (initialized)
-			printk(CDU535_MESSAGE_NAME " error: drive busy\n");
-		return CD_BUSY;
-	case SONY535_ATN_EJECT_IN_PROGRESS:
-		printk(CDU535_MESSAGE_NAME " error: eject in progress\n");
-		sony_audio_status = CDROM_AUDIO_INVALID;
-		return CD_BUSY;
-	case SONY535_ATN_RESET_OCCURRED:
-	case SONY535_ATN_DISC_CHANGED:
-	case SONY535_ATN_RESET_AND_DISC_CHANGED:
-#if DEBUG > 0
-		printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n");
-#endif
-		sony_disc_changed = 1;
-		sony_toc_read = 0;
-		sony_audio_status = CDROM_AUDIO_NO_STATUS;
-		sony_first_block = -1;
-		sony_last_block = -1;
-		if (initialized) {
-			cmd = SONY535_SPIN_UP;
-			do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0);
-			sony_get_toc();
-		}
-		return 0;
-	default:
-		printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN);
-		return CD_BUSY;
-	}
-	switch (CDD) {			/* the 531 docs are not helpful in decoding this */
-	case 0x0:				/* just use the values from the DOS driver */
-	case 0x2:
-	case 0xa:
-		break;				/* no error */
-	case 0xc:
-		printk(CDU535_MESSAGE_NAME
-				": check_drive_status(): CDD = 0xc! Not properly handled!\n");
-		return CD_BUSY;		/* ? */
-	default:
-		return CD_BUSY;
-	}
-	return 0;
-}	/* check_drive_status() */
-
-/*****************************************************************************
- * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2],
- *                Byte *response, int n_response, int ignore_status_bit7 )
- *
- *  Generic routine for executing commands.  The command and its parameters
- *  should be placed in the cmd[] array, number of bytes in the command is
- *  stored in nCmd.  The response from the command will be stored in the
- *  response array.  The number of bytes you expect back (excluding status)
- *  should be passed in n_response.  Finally, some
- *  commands set bit 7 of the return status even when there is no second
- *  status byte, on these commands set ignoreStatusBit7 TRUE.
- *    If the command was sent and data received back, then we return 0,
- *  else we return TIME_OUT.  You still have to check the status yourself.
- *    You should call check_drive_status() before calling this routine
- *  so that you do not lose notifications of disk changes, etc.
- ****************************************************************************/
-static int
-do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2],
-			Byte * response, int n_response, int ignore_status_bit7)
-{
-	int i;
-
-	/* write out the command */
-	for (i = 0; i < n_cmd; i++)
-		outb(cmd[i], command_reg);
-
-	/* read back the status */
-	if (read_result_reg(status) != 0)
-		return TIME_OUT;
-	if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) {
-		/* get second status byte */
-		if (read_result_reg(status + 1) != 0)
-			return TIME_OUT;
-	} else {
-		status[1] = 0;
-	}
-#if DEBUG > 2
-	printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n",
-			*cmd, status[0], status[1]);
-#endif
-
-	/* do not know about when I should read set of data and when not to */
-	if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0)
-		return 0;
-
-	/* else, read in rest of data */
-	for (i = 0; 0 < n_response; n_response--, i++)
-		if (read_result_reg(response + i) != 0)
-			return TIME_OUT;
-	return 0;
-}	/* do_sony_cmd() */
-
-/**************************************************************************
- * int set_drive_mode( int mode, Byte status[2] )
- *
- *  Set the drive mode to the specified value (mode=0 is audio, mode=e0
- * is mode-1 CDROM
- **************************************************************************/
-static int
-set_drive_mode(int mode, Byte status[2])
-{
-	Byte cmd_buff[2];
-	Byte ret_buff[1];
-
-	cmd_buff[0] = SONY535_SET_DRIVE_MODE;
-	cmd_buff[1] = mode;
-	return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1);
-}
-
-/***************************************************************************
- * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2],
- *                             Byte *data_buff, int buff_size )
- *
- *  Read n_blocks of data from the CDROM starting at position params[0:2],
- *  number of blocks in stored in params[3:5] -- both these are already
- *  int bcd format.
- *  Transfer the data into the buffer pointed at by data_buff.  buff_size
- *  gives the number of bytes available in the buffer.
- *    The routine returns number of bytes read in if successful, otherwise
- *  it returns one of the standard error returns.
- ***************************************************************************/
-static int
-seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
-					   Byte **buff, int buf_size)
-{
-	const int block_size = 2048;
-	Byte cmd_buff[7];
-	int  i;
-	int  read_status;
-	int  retry_count;
-	Byte *data_buff;
-	int  sector_count = 0;
-
-	if (buf_size < ((long)block_size) * n_blocks)
-		return NO_ROOM;
-
-	set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
-
-	/* send command to read the data */
-	cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
-	for (i = 0; i < 6; i++)
-		cmd_buff[i + 1] = params[i];
-	for (i = 0; i < 7; i++)
-		outb(cmd_buff[i], command_reg);
-
-	/* read back the data one block at a time */
-	while (0 < n_blocks--) {
-		/* wait for data to be ready */
-		retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-		while (jiffies < retry_count) {
-			read_status = inb(read_status_reg);
-			if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
-				read_exec_status(status);
-				return BAD_STATUS;
-			}
-			if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
-				/* data is ready, read it */
-				data_buff = buff[sector_count++];
-				for (i = 0; i < block_size; i++)
-					*data_buff++ = inb(data_reg);	/* unrolling this loop does not seem to help */
-				break;			/* exit the timeout loop */
-			}
-			sony_sleep();		/* data not ready, sleep a while */
-		}
-		if (retry_count <= jiffies)
-			return TIME_OUT;	/* if we reach this stage */
-	}
-
-	/* read all the data, now read the status */
-	if ((i = read_exec_status(status)) != 0)
-		return i;
-	return block_size * sector_count;
-}	/* seek_and_read_N_blocks() */
-
-/****************************************************************************
- * int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
- *
- *  Read in the table of contents data.  Converts all the bcd data
- * into integers in the toc structure.
- ****************************************************************************/
-static int
-request_toc_data(Byte status[2], struct s535_sony_toc *toc)
-{
-	int  to_status;
-	int  i, j, n_tracks, track_no;
-	int  first_track_num, last_track_num;
-	Byte cmd_no = 0xb2;
-	Byte track_address_buffer[5];
-
-	/* read the fixed portion of the table of contents */
-	if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0)
-		return to_status;
-
-	/* convert the data into integers so we can use them */
-	first_track_num = bcd_to_int(toc->first_track_num);
-	last_track_num = bcd_to_int(toc->last_track_num);
-	n_tracks = last_track_num - first_track_num + 1;
-
-	/* read each of the track address descriptors */
-	for (i = 0; i < n_tracks; i++) {
-		/* read the descriptor into a temporary buffer */
-		for (j = 0; j < 5; j++) {
-			if (read_result_reg(track_address_buffer + j) != 0)
-				return TIME_OUT;
-			if (j == 1)		/* need to convert from bcd */
-				track_no = bcd_to_int(track_address_buffer[j]);
-		}
-		/* copy the descriptor to proper location - sonycd.c just fills */
-		memcpy(toc->tracks + i, track_address_buffer, 5);
-	}
-	return 0;
-}	/* request_toc_data() */
-
-/***************************************************************************
- * int spin_up_drive( Byte status[2] )
- *
- *  Spin up the drive (unless it is already spinning).
- ***************************************************************************/
-static int
-spin_up_drive(Byte status[2])
-{
-	Byte cmd;
-
-	/* first see if the drive is already spinning */
-	cmd = SONY535_REQUEST_DRIVE_STATUS_1;
-	if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0)
-		return TIME_OUT;
-	if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0)
-		return 0;	/* it's already spinning */
-
-	/* otherwise, give the spin-up command */
-	cmd = SONY535_SPIN_UP;
-	return do_sony_cmd(&cmd, 1, status, NULL, 0, 0);
-}
-
-/*--------------------end of SONY CDU535 very specific ---------------------*/
-
-/* Convert from an integer 0-99 to BCD */
-static inline unsigned int
-int_to_bcd(unsigned int val)
-{
-	int retval;
-
-	retval = (val / 10) << 4;
-	retval = retval | val % 10;
-	return retval;
-}
-
-
-/* Convert from BCD to an integer from 0-99 */
-static unsigned int
-bcd_to_int(unsigned int bcd)
-{
-	return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f);
-}
-
-
-/*
- * Convert a logical sector value (like the OS would want to use for
- * a block device) to an MSF format.
- */
-static void
-log_to_msf(unsigned int log, Byte *msf)
-{
-	log = log + LOG_START_OFFSET;
-	msf[0] = int_to_bcd(log / 4500);
-	log = log % 4500;
-	msf[1] = int_to_bcd(log / 75);
-	msf[2] = int_to_bcd(log % 75);
-}
-
-
-/*
- * Convert an MSF format to a logical sector.
- */
-static unsigned int
-msf_to_log(Byte *msf)
-{
-	unsigned int log;
-
-
-	log = bcd_to_int(msf[2]);
-	log += bcd_to_int(msf[1]) * 75;
-	log += bcd_to_int(msf[0]) * 4500;
-	log = log - LOG_START_OFFSET;
-
-	return log;
-}
-
-
-/*
- * Take in integer size value and put it into a buffer like
- * the drive would want to see a number-of-sector value.
- */
-static void
-size_to_buf(unsigned int size, Byte *buf)
-{
-	buf[0] = size / 65536;
-	size = size % 65536;
-	buf[1] = size / 256;
-	buf[2] = size % 256;
-}
-
-
-/*
- * 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
- * bytes to help speed operations.  This especially helps since the OS
- * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
- * data access on a CD is done sequentially, this saves a lot of operations.
- */
-static void
-do_cdu535_request(void)
-{
-	unsigned int dev;
-	unsigned int read_size;
-	int  block;
-	int  nsect;
-	int  copyoff;
-	int  spin_up_retry;
-	Byte params[10];
-	Byte status[2];
-	Byte cmd[2];
-
-	if (!sony_inuse) {
-		cdu_open(NULL, NULL);
-	}
-	while (1) {
-		/*
-		 * The beginning here is stolen from the hard disk driver.  I hope
-		 * it's right.
-		 */
-		if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) {
-			return;
-		}
-		INIT_REQUEST;
-		dev = MINOR(CURRENT->rq_dev);
-		block = CURRENT->sector;
-		nsect = CURRENT->nr_sectors;
-		if (dev != 0) {
-			end_request(0);
-			continue;
-		}
-		switch (CURRENT->cmd) {
-		case READ:
-			/*
-			 * If the block address is invalid or the request goes beyond the end of
-			 * the media, return an error.
-			 */
-
-			if (sony_toc->lead_out_start_lba <= (block / 4)) {
-				end_request(0);
-				return;
-			}
-			if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) {
-				end_request(0);
-				return;
-			}
-			while (0 < nsect) {
-				/*
-				 * If the requested sector is not currently in the read-ahead buffer,
-				 * it must be read in.
-				 */
-				if ((block < sony_first_block) || (sony_last_block < block)) {
-					sony_first_block = (block / 4) * 4;
-					log_to_msf(block / 4, params);
-
-					/*
-					 * If the full read-ahead would go beyond the end of the media, trim
-					 * it back to read just till the end of the media.
-					 */
-					if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) {
-						sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
-						read_size = sony_toc->lead_out_start_lba - (block / 4);
-					} else {
-						sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
-						read_size = sony_buffer_sectors;
-					}
-					size_to_buf(read_size, &params[3]);
-
-					/*
-					 * Read the data.  If the drive was not spinning,
-					 * spin it up and try some more.
-					 */
-					for (spin_up_retry=0 ;; ++spin_up_retry) {
-						/* This loop has been modified to support the Sony
-						 * CDU-510/515 series, thanks to Claudio Porfiri 
-						 * <C.Porfiri@nisms.tei.ericsson.se>.
-						 */
-						/*
-						 * This part is to deal with very slow hardware.  We
-						 * try at most MAX_SPINUP_RETRY times to read the same
-						 * block.  A check for seek_and_read_N_blocks' result is
-						 * performed; if the result is wrong, the CDROM's engine
-						 * is restarted and the operation is tried again.
-						 */
-						/*
-						 * 1995-06-01: The system got problems when downloading
-						 * from Slackware CDROM, the problem seems to be:
-						 * seek_and_read_N_blocks returns BAD_STATUS and we
-						 * should wait for a while before retrying, so a new
-						 * part was added to discriminate the return value from
-						 * seek_and_read_N_blocks for the various cases.
-						 */
-						int readStatus = seek_and_read_N_blocks(params, read_size,
-									status, sony_buffer, (read_size * 2048));
-						if (0 <= readStatus)	/* Good data; common case, placed first */
-							break;
-						if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) {
-							/* give up */
-							if (readStatus == NO_ROOM)
-								printk(CDU535_MESSAGE_NAME " No room to read from CD\n");
-							else
-								printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n",
-										status[0]);
-							sony_first_block = -1;
-							sony_last_block = -1;
-							end_request(0);
-							return;
-						}
-						if (readStatus == BAD_STATUS) {
-							/* Sleep for a while, then retry */
-							current->state = TASK_INTERRUPTIBLE;
-							current->timeout = jiffies + RETRY_FOR_BAD_STATUS;
-							schedule();
-						}
-#if DEBUG > 0
-						printk(CDU535_MESSAGE_NAME
-							" debug: calling spin up when reading data!\n");
-#endif
-						cmd[0] = SONY535_SPIN_UP;
-						do_sony_cmd(cmd, 1, status, NULL, 0, 0);
-					}
-				}
-				/*
-				 * The data is in memory now, copy it to the buffer and advance to the
-				 * next block to read.
-				 */
-				copyoff = block - sony_first_block;
-				memcpy(CURRENT->buffer,
-					   sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512);
-
-				block += 1;
-				nsect -= 1;
-				CURRENT->buffer += 512;
-			}
-
-			end_request(1);
-			break;
-
-		case WRITE:
-			end_request(0);
-			break;
-
-		default:
-			panic("Unknown SONY CD cmd");
-		}
-	}
-}
-
-
-/*
- * Read the table of contents from the drive and set sony_toc_read if
- * successful.
- */
-static void
-sony_get_toc(void)
-{
-	Byte status[2];
-	if (!sony_toc_read) {
-		/* do not call check_drive_status() from here since it can call this routine */
-		if (request_toc_data(status, sony_toc) < 0)
-			return;
-		sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
-		sony_toc_read = 1;
-	}
-}
-
-
-/*
- * Search for a specific track in the table of contents.  track is
- * passed in bcd format
- */
-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++) {
-		if (sony_toc->tracks[i].track == track) {
-			return i;
-		}
-	}
-
-	return -1;
-}
-
-/*
- * Read the subcode and put it int last_sony_subcode for future use.
- */
-static int
-read_subcode(void)
-{
-	Byte cmd = SONY535_REQUEST_SUB_Q_DATA;
-	Byte status[2];
-	int  dsc_status;
-
-	if (check_drive_status() != 0)
-		return -EIO;
-
-	if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode,
-							   sizeof(struct s535_sony_subcode), 1)) != 0) {
-		printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n",
-				status[0], dsc_status);
-		return -EIO;
-	}
-	return 0;
-}
-
-
-/*
- * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
- * the drive is playing, the subchannel needs to be read (since it would be
- * changing).  If the drive is paused or completed, the subcode information has
- * already been stored, just use that.  The ioctl call wants things in decimal
- * (not BCD), so all the conversions are done.
- */
-static int
-sony_get_subchnl_info(long arg)
-{
-	struct cdrom_subchnl schi;
-	int err;
-
-	/* Get attention stuff */
-	if (check_drive_status() != 0)
-		return -EIO;
-
-	sony_get_toc();
-	if (!sony_toc_read) {
-		return -EIO;
-	}
-	err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi);
-	if (err)
-		return err;
-
-	memcpy_fromfs(&schi, (char *)arg, sizeof schi);
-
-	switch (sony_audio_status) {
-	case CDROM_AUDIO_PLAY:
-		if (read_subcode() < 0) {
-			return -EIO;
-		}
-		break;
-
-	case CDROM_AUDIO_PAUSED:
-	case CDROM_AUDIO_COMPLETED:
-		break;
-
-	case CDROM_AUDIO_NO_STATUS:
-		schi.cdsc_audiostatus = sony_audio_status;
-		memcpy_tofs((char *)arg, &schi, sizeof schi);
-		return 0;
-		break;
-
-	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]);
-	} 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);
-	}
-	memcpy_tofs((char *)arg, &schi, sizeof schi);
-	return 0;
-}
-
-
-/*
- * The big ugly ioctl handler.
- */
-static int
-cdu_ioctl(struct inode *inode,
-		  struct file *file,
-		  unsigned int cmd,
-		  unsigned long arg)
-{
-	unsigned int dev;
-	Byte status[2];
-	Byte cmd_buff[10], params[10];
-	int  i;
-	int  dsc_status;
-	int  err;
-
-	if (!inode) {
-		return -EINVAL;
-	}
-	dev = MINOR(inode->i_rdev) >> 6;
-	if (dev != 0) {
-		return -EINVAL;
-	}
-	if (check_drive_status() != 0)
-		return -EIO;
-
-	switch (cmd) {
-	case CDROMSTART:			/* Spin up the drive */
-		if (spin_up_drive(status) < 0) {
-			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n",
-					status[0]);
-			return -EIO;
-		}
-		return 0;
-		break;
-
-	case CDROMSTOP:			/* Spin down the drive */
-		cmd_buff[0] = SONY535_HOLD;
-		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-
-		/*
-		 * Spin the drive down, ignoring the error if the disk was
-		 * already not spinning.
-		 */
-		sony_audio_status = CDROM_AUDIO_NO_STATUS;
-		cmd_buff[0] = SONY535_SPIN_DOWN;
-		dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-		if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) ||
-			((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) {
-			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n",
-					status[0]);
-			return -EIO;
-		}
-		return 0;
-		break;
-
-	case CDROMPAUSE:			/* Pause the drive */
-		cmd_buff[0] = SONY535_HOLD;		/* CDU-31 driver uses AUDIO_STOP, not pause */
-		if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
-			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n",
-					status[0]);
-			return -EIO;
-		}
-		/* Get the current position and save it for resuming */
-		if (read_subcode() < 0) {
-			return -EIO;
-		}
-		cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
-		cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
-		cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
-		sony_audio_status = CDROM_AUDIO_PAUSED;
-		return 0;
-		break;
-
-	case CDROMRESUME:			/* Start the drive after being paused */
-		set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
-
-		if (sony_audio_status != CDROM_AUDIO_PAUSED) {
-			return -EINVAL;
-		}
-		spin_up_drive(status);
-
-		/* Start the drive at the saved position. */
-		cmd_buff[0] = SONY535_PLAY_AUDIO;
-		cmd_buff[1] = 0;		/* play back starting at this address */
-		cmd_buff[2] = cur_pos_msf[0];
-		cmd_buff[3] = cur_pos_msf[1];
-		cmd_buff[4] = cur_pos_msf[2];
-		cmd_buff[5] = SONY535_PLAY_AUDIO;
-		cmd_buff[6] = 2;		/* set ending address */
-		cmd_buff[7] = final_pos_msf[0];
-		cmd_buff[8] = final_pos_msf[1];
-		cmd_buff[9] = final_pos_msf[2];
-		if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
-			(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
-			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n",
-					status[0]);
-			return -EIO;
-		}
-		sony_audio_status = CDROM_AUDIO_PLAY;
-		return 0;
-		break;
-
-	case CDROMPLAYMSF:			/* Play starting at the given MSF address. */
-		err = verify_area(VERIFY_READ, (char *)arg, 6);
-		if (err)
-			return err;
-		spin_up_drive(status);
-		set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
-		memcpy_fromfs(params, (void *)arg, 6);
-
-		/* The parameters are given in int, must be converted */
-		for (i = 0; i < 3; i++) {
-			cmd_buff[2 + i] = int_to_bcd(params[i]);
-			cmd_buff[7 + i] = int_to_bcd(params[i + 3]);
-		}
-		cmd_buff[0] = SONY535_PLAY_AUDIO;
-		cmd_buff[1] = 0;		/* play back starting at this address */
-		/* cmd_buff[2-4] are filled in for loop above */
-		cmd_buff[5] = SONY535_PLAY_AUDIO;
-		cmd_buff[6] = 2;		/* set ending address */
-		/* cmd_buff[7-9] are filled in for loop above */
-		if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
-			(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
-			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n",
-					status[0]);
-			return -EIO;
-		}
-		/* Save the final position for pauses and resumes */
-		final_pos_msf[0] = cmd_buff[7];
-		final_pos_msf[1] = cmd_buff[8];
-		final_pos_msf[2] = cmd_buff[9];
-		sony_audio_status = CDROM_AUDIO_PLAY;
-		return 0;
-		break;
-
-	case CDROMREADTOCHDR:		/* Read the table of contents header */
-		{
-			struct cdrom_tochdr *hdr;
-			struct cdrom_tochdr loc_hdr;
-
-			sony_get_toc();
-			if (!sony_toc_read)
-				return -EIO;
-			hdr = (struct cdrom_tochdr *)arg;
-			err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr);
-			if (err)
-				return err;
-			loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
-			loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
-			memcpy_tofs(hdr, &loc_hdr, sizeof *hdr);
-		}
-		return 0;
-		break;
-
-	case CDROMREADTOCENTRY:	/* Read a given table of contents entry */
-		{
-			struct cdrom_tocentry *entry;
-			struct cdrom_tocentry loc_entry;
-			int  track_idx;
-			Byte *msf_val = NULL;
-
-			sony_get_toc();
-			if (!sony_toc_read) {
-				return -EIO;
-			}
-			entry = (struct cdrom_tocentry *)arg;
-			err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry);
-			if (err)
-				return err;
-
-			memcpy_fromfs(&loc_entry, entry, sizeof loc_entry);
-
-			/* Lead out is handled separately since it is special. */
-			if (loc_entry.cdte_track == CDROM_LEADOUT) {
-				loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ;
-				loc_entry.cdte_ctrl = sony_toc->control2;
-				msf_val = sony_toc->lead_out_start_msf;
-			} else {
-				track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
-				if (track_idx < 0)
-					return -EINVAL;
-				loc_entry.cdte_adr = 0 /*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;
-			}
-
-			/* Logical buffer address or MSF format requested? */
-			if (loc_entry.cdte_format == CDROM_LBA) {
-				loc_entry.cdte_addr.lba = msf_to_log(msf_val);
-			} else if (loc_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));
-			}
-			memcpy_tofs(entry, &loc_entry, sizeof *entry);
-		}
-		return 0;
-		break;
-
-	case CDROMPLAYTRKIND:		/* Play a track.  This currently ignores index. */
-		{
-			struct cdrom_ti ti;
-			int track_idx;
-
-			sony_get_toc();
-			if (!sony_toc_read)
-				return -EIO;
-			err = verify_area(VERIFY_READ, (char *)arg, sizeof ti);
-			if (err)
-				return err;
-
-			memcpy_fromfs(&ti, (char *)arg, sizeof ti);
-			if ((ti.cdti_trk0 < sony_toc->first_track_num)
-				|| (sony_toc->last_track_num < ti.cdti_trk0)
-				|| (ti.cdti_trk1 < ti.cdti_trk0)) {
-				return -EINVAL;
-			}
-			track_idx = find_track(int_to_bcd(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];
-			/*
-			 * If we want to stop after the last track, use the lead-out
-			 * MSF to do that.
-			 */
-			if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) {
-				log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1,
-						   &(params[4]));
-			} 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]));
-			}
-			params[0] = 0x03;
-
-			spin_up_drive(status);
-
-			set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
-
-			/* Start the drive at the saved position. */
-			cmd_buff[0] = SONY535_PLAY_AUDIO;
-			cmd_buff[1] = 0;	/* play back starting at this address */
-			cmd_buff[2] = params[1];
-			cmd_buff[3] = params[2];
-			cmd_buff[4] = params[3];
-			cmd_buff[5] = SONY535_PLAY_AUDIO;
-			cmd_buff[6] = 2;	/* set ending address */
-			cmd_buff[7] = params[4];
-			cmd_buff[8] = params[5];
-			cmd_buff[9] = params[6];
-			if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
-				(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
-				printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n",
-						status[0]);
-				printk("... Params: %x %x %x %x %x %x %x\n",
-						params[0], params[1], params[2],
-						params[3], params[4], params[5], params[6]);
-				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];
-			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;
-
-			err = verify_area(VERIFY_READ, (char *)arg, sizeof volctrl);
-			if (err)
-				return err;
-
-			memcpy_fromfs(&volctrl, (char *)arg, sizeof volctrl);
-			cmd_buff[0] = SONY535_SET_VOLUME;
-			cmd_buff[1] = volctrl.channel0;
-			cmd_buff[2] = volctrl.channel1;
-			if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) {
-				printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n",
-						status[0]);
-				return -EIO;
-			}
-		}
-		return 0;
-
-	case CDROMEJECT:			/* Eject the drive */
-		cmd_buff[0] = SONY535_STOP;
-		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-		cmd_buff[0] = SONY535_SPIN_DOWN;
-		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-
-		sony_audio_status = CDROM_AUDIO_INVALID;
-		cmd_buff[0] = SONY535_EJECT_CADDY;
-		if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
-			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n",
-					status[0]);
-			return -EIO;
-		}
-		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
-cdu_open(struct inode *inode,
-		 struct file *filp)
-{
-	Byte status[2], cmd_buff[2];
-
-
-	if (sony_inuse)
-		return -EBUSY;
-	if (check_drive_status() != 0)
-		return -EIO;
-	sony_inuse = 1;
-	MOD_INC_USE_COUNT;
-
-	if (spin_up_drive(status) != 0) {
-		printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n",
-				status[0]);
-		sony_inuse = 0;
-		MOD_DEC_USE_COUNT;
-		return -EIO;
-	}
-	sony_get_toc();
-	if (!sony_toc_read) {
-		cmd_buff[0] = SONY535_SPIN_DOWN;
-		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-		sony_inuse = 0;
-		MOD_DEC_USE_COUNT;
-		return -EIO;
-	}
-	if (inode) {
-		check_disk_change(inode->i_rdev);
-	}
-	sony_usage++;
-
-#ifdef LOCK_DOORS
-	/* disable the eject button while mounted */
-	cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON;
-	do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-#endif
-
-	return 0;
-}
-
-
-/*
- * 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 void
-cdu_release(struct inode *inode,
-			struct file *filp)
-{
-	Byte status[2], cmd_no;
-
-	sony_inuse = 0;
-	MOD_DEC_USE_COUNT;
-
-	if (0 < sony_usage) {
-		sony_usage--;
-	}
-	if (sony_usage == 0) {
-		sync_dev(inode->i_rdev);
-		check_drive_status();
-
-		if (sony_audio_status != CDROM_AUDIO_PLAY) {
-			cmd_no = SONY535_SPIN_DOWN;
-			do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
-		}
-#ifdef LOCK_DOORS
-		/* enable the eject button after umount */
-		cmd_no = SONY535_ENABLE_EJECT_BUTTON;
-		do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
-#endif
-	}
-}
-
-
-static struct file_operations cdu_fops =
-{
-	NULL,						/* lseek - default */
-	block_read,					/* read - general block-dev read */
-	block_write,				/* write - general block-dev write */
-	NULL,						/* readdir - bad */
-	NULL,						/* select */
-	cdu_ioctl,					/* ioctl */
-	NULL,						/* mmap */
-	cdu_open,					/* open */
-	cdu_release,				/* release */
-	NULL,						/* fsync */
-	NULL,						/* fasync */
-	cdu535_check_media_change,	/* check media change */
-	NULL						/* revalidate */
-};
-
-/*
- * Initialize the driver.
- */
-int
-sony535_init(void)
-{
-	struct s535_sony_drive_config drive_config;
-	Byte cmd_buff[3];
-	Byte ret_buff[2];
-	Byte status[2];
-	int  retry_count;
-	int  tmp_irq;
-	int  i;
-
-	/* Setting the base I/O address to 0 will disable it. */
-	if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0))
-		return 0;
-
-	/* Set up all the register locations */
-	result_reg = sony535_cd_base_io;
-	command_reg = sony535_cd_base_io;
-	data_reg = sony535_cd_base_io + 1;
-	read_status_reg = sony535_cd_base_io + 2;
-	select_unit_reg = sony535_cd_base_io + 3;
-
-#ifndef USE_IRQ
-	sony535_irq_used = 0;	/* polling only until this is ready... */
-#endif
-	/* we need to poll until things get initialized */
-	tmp_irq = sony535_irq_used;
-	sony535_irq_used = 0;
-
-#if DEBUG > 0
-	printk(CDU535_MESSAGE_NAME ": probing base address %03X\n",
-			sony535_cd_base_io);
-#endif
-	if (check_region(sony535_cd_base_io,4)) {
-		printk(CDU535_MESSAGE_NAME ": my base address is not free!\n");
-		return -EIO;
-	}
-	/* look for the CD-ROM, follows the procedure in the DOS driver */
-	inb(select_unit_reg);
-	retry_count = jiffies + 2 * HZ;
-	while (jiffies < retry_count)
-		sony_sleep();			/* wait for 40 18 Hz ticks (from DOS driver) */
-	inb(result_reg);
-
-	outb(0, read_status_reg);	/* does a reset? */
-	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-	while (jiffies < retry_count) {
-		select_unit(0);
-		if (inb(result_reg) != 0xff)
-			break;
-		sony_sleep();
-	}
-
-	if ((jiffies < retry_count) && (check_drive_status() != TIME_OUT)) {
-		/* CD-ROM drive responded --  get the drive configuration */
-		cmd_buff[0] = SONY535_INQUIRY;
-		if (do_sony_cmd(cmd_buff, 1, status,
-						(Byte *)&drive_config, 28, 1) == 0) {
-			/* was able to get the configuration,
-			 * set drive mode as rest of init
-			 */
-#if DEBUG > 0
-			/* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */
-			if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 )
-				printk(CDU535_MESSAGE_NAME
-						"Inquiry command returned status = 0x%x\n", status[0]);
-#endif
-			/* now ready to use interrupts, if available */
-			sony535_irq_used = tmp_irq;
-#ifndef MODULE
-/* This code is not in MODULEs by default, since the autoirq stuff might
- * not be in the module-accessible symbol table.
- */
-			/* A negative sony535_irq_used will attempt an autoirq. */
-			if (sony535_irq_used < 0) {
-				autoirq_setup(0);
-				enable_interrupts();
-				outb(0, read_status_reg);	/* does a reset? */
-				sony535_irq_used = autoirq_report(10);
-				disable_interrupts();
-			}
-#endif
-			if (sony535_irq_used > 0) {
-			    if (request_irq(sony535_irq_used, cdu535_interrupt,
-								SA_INTERRUPT, CDU535_HANDLE)) {
-					printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME
-							" driver; polling instead.\n", sony535_irq_used);
-					sony535_irq_used = 0;
-				}
-			}
-			cmd_buff[0] = SONY535_SET_DRIVE_MODE;
-			cmd_buff[1] = 0x0;	/* default audio */
-			if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
-				/* set the drive mode successful, we are set! */
-				sony_buffer_size = SONY535_BUFFER_SIZE;
-				sony_buffer_sectors = sony_buffer_size / 2048;
-
-				printk(CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
-					   drive_config.vendor_id,
-					   drive_config.product_id,
-					   drive_config.product_rev_level);
-				printk("  base address %03X, ", sony535_cd_base_io);
-				if (tmp_irq > 0)
-					printk("IRQ%d, ", tmp_irq);
-				printk("using %d byte buffer\n", sony_buffer_size);
-
-				if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
-					printk("Unable to get major %d for %s\n",
-							MAJOR_NR, CDU535_MESSAGE_NAME);
-					return -EIO;
-				}
-				blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-				read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read-ahead */
-
-				sony_toc = (struct s535_sony_toc *)
-					kmalloc(sizeof *sony_toc, GFP_KERNEL);
-				if (sony_toc == NULL)
-					return -ENOMEM;
-				last_sony_subcode = (struct s535_sony_subcode *)
-					kmalloc(sizeof *last_sony_subcode, GFP_KERNEL);
-				if (last_sony_subcode == NULL) {
-					kfree(sony_toc);
-					return -ENOMEM;
-				}
-				sony_buffer = (Byte **)
-					kmalloc(4 * sony_buffer_sectors, GFP_KERNEL);
-				if (sony_buffer == NULL) {
-					kfree(sony_toc);
-					kfree(last_sony_subcode);
-					return -ENOMEM;
-				}
-				for (i = 0; i < sony_buffer_sectors; i++) {
-					sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL);
-					if (sony_buffer[i] == NULL) {
-						while (--i>=0)
-							kfree(sony_buffer[i]);
-						kfree(sony_buffer);
-						kfree(sony_toc);
-						kfree(last_sony_subcode);
-						return -ENOMEM;
-					}
-				}
-				initialized = 1;
-			}
-		}
-	}
-
-	if (!initialized) {
-		printk("Did not find a " CDU535_MESSAGE_NAME " drive\n");
-		return -EIO;
-	}
-	request_region(sony535_cd_base_io, 4, CDU535_HANDLE);
-	return 0;
-}
-
-#ifndef MODULE
-/*
- * accept "kernel command line" parameters
- * (added by emoenke@gwdg.de)
- *
- * use: tell LILO:
- *                 sonycd535=0x320
- *
- * the address value has to be the existing CDROM port address.
- */
-void
-sonycd535_setup(char *strings, int *ints)
-{
-	/* if IRQ change and default io base desired,
-	 * then call with io base of 0
-	 */
-	if (ints[0] > 0)
-		if (ints[0] != 0)
-			sony535_cd_base_io = ints[1];
-	if (ints[0] > 1)
-		sony535_irq_used = ints[2];
-	if ((strings != NULL) && (*strings != '\0'))
-		printk(CDU535_MESSAGE_NAME
-				": Warning: Unknown interface type: %s\n", strings);
-}
-
-#else /* MODULE */
-
-void
-cleanup_module(void)
-{
-	int i;
-	if (MOD_IN_USE) {
-		printk(CDU535_HANDLE " module in use, cannot remove\n");
-		return;
-	}
-	release_region(sony535_cd_base_io, 4);
-	for (i = 0; i < sony_buffer_sectors; i++)
-		kfree_s(sony_buffer[i], 2048);
-	kfree_s(sony_buffer, 4 * sony_buffer_sectors);
-	kfree_s(last_sony_subcode, sizeof *last_sony_subcode);
-	kfree_s(sony_toc, sizeof *sony_toc);
-	if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
-		printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
-	else
-		printk(CDU535_HANDLE " module released\n");
-}
-#endif	/* MODULE */
diff -u --recursive --new-file v1.3.35/linux/drivers/block/xd.c linux/drivers/block/xd.c
--- v1.3.35/linux/drivers/block/xd.c	Tue Oct 10 18:46:33 1995
+++ linux/drivers/block/xd.c	Sat Oct 21 19:37:35 1995
@@ -34,7 +34,7 @@
 #include <asm/dma.h>
 
 #define MAJOR_NR XT_DISK_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
 
 XD_INFO xd_info[XD_MAXDRIVES];
 
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/Config.in linux/drivers/cdrom/Config.in
--- v1.3.35/linux/drivers/cdrom/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/Config.in	Sun Oct 22 16:02:13 1995
@@ -0,0 +1,22 @@
+#
+# CDROM driver configuration
+#
+tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A
+tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD
+tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX
+tristate 'Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD
+if [ "$CONFIG_SBPCD" = "y" ]; then
+  bool 'Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2
+  if [ "$CONFIG_SBPCD2" = "y" ]; then
+    bool 'Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3
+    if [ "$CONFIG_SBPCD3" = "y" ]; then
+      bool 'Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4
+    fi
+  fi
+fi
+tristate 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD
+tristate 'Sony CDU535 CDROM support' CONFIG_CDU535
+tristate 'Goldstar R420 CDROM support' CONFIG_GSCD
+tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206
+tristate 'Experimental Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD
+tristate 'Experimental Sanyo H94A CDROM support' CONFIG_SJCD
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile
--- v1.3.35/linux/drivers/cdrom/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/Makefile	Sat Oct 21 17:51:35 1995
@@ -0,0 +1,115 @@
+#
+# Makefile for the kernel cdrom device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+#
+# Note : at this point, these files are compiled on all systems. 
+# In the future, some of these should be built conditionally.
+#
+
+
+L_TARGET := cdrom.a
+L_OBJS   :=
+M_OBJS   :=
+MOD_LIST_NAME := CDROM_MODULES
+
+ifeq ($(CONFIG_AZTCD),y)
+L_OBJS += aztcd.o
+else
+  ifeq ($(CONFIG_AZTCD),m)
+  M_OBJS += aztcd.o
+  endif
+endif #CONFIG_AZTCD
+
+ifeq ($(CONFIG_CDU31A),y)
+L_OBJS += cdu31a.o
+else
+  ifeq ($(CONFIG_CDU31A),m)
+  M_OBJS += cdu31a.o
+  endif
+endif #CONFIG_CDU31A
+
+ifeq ($(CONFIG_MCD),y)
+L_OBJS += mcd.o
+else
+  ifeq ($(CONFIG_MCD),m)
+  M_OBJS += mcd.o
+  endif
+endif #CONFIG_MCD
+
+ifeq ($(CONFIG_MCDX),y)
+L_OBJS += mcdx.o
+else
+  ifeq ($(CONFIG_MCDX),m)
+  M_OBJS += mcdx.o
+  endif
+endif #CONFIG_MCDX
+
+ifeq ($(CONFIG_SBPCD),y)
+L_OBJS += sbpcd.o
+else
+  ifeq ($(CONFIG_SBPCD),m)
+  M_OBJS += sbpcd.o
+  endif
+endif #CONFIG_SBPCD
+
+ifeq ($(CONFIG_SBPCD2),y)
+L_OBJS += sbpcd2.o
+endif #CONFIG_SBPCD2
+
+ifeq ($(CONFIG_SBPCD3),y)
+L_OBJS += sbpcd3.o
+endif #CONFIG_SBPCD3
+
+ifeq ($(CONFIG_SBPCD4),y)
+L_OBJS += sbpcd4.o
+endif #CONFIG_SBPCD4
+
+ifeq ($(CONFIG_CDU535),y)
+L_OBJS += sonycd535.o
+else
+  ifeq ($(CONFIG_CDU535),m)
+  M_OBJS += sonycd535.o
+  endif
+endif #CONFIG_CDU535
+
+ifeq ($(CONFIG_GSCD),y)
+L_OBJS += gscd.o
+else
+  ifeq ($(CONFIG_GSCD),m)
+  M_OBJS += gscd.o
+  endif
+endif #CONFIG_GSCD
+
+ifeq ($(CONFIG_CM206),y)
+L_OBJS += cm206.o
+else
+  ifeq ($(CONFIG_CM206),m)
+  M_OBJS += cm206.o
+  endif
+endif #CONFIG_CM206
+
+ifeq ($(CONFIG_OPTCD),y)
+L_OBJS += optcd.o
+else
+  ifeq ($(CONFIG_OPTCD),m)
+  M_OBJS += optcd.o
+  endif
+endif #CONFIG_OPTCD
+
+ifeq ($(CONFIG_SJCD),y)
+L_OBJS += sjcd.o
+#else
+#  ifeq ($(CONFIG_SJCD),m)
+#  M_OBJS += sjcd.o
+#  endif
+endif #CONFIG_SJCD
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v1.3.35/linux/drivers/cdrom/aztcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/aztcd.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,2087 @@
+#define AZT_VERSION "1.80"
+/*      $Id: aztcd.c,v 1.80 1995/10/11 19:35:03 root Exp root $
+	linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
+
+	Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+
+	based on Mitsumi CDROM driver by  Martin Hariss and preworks by
+	Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby 
+	Schirmer.
+
+	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; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	HISTORY
+	V0.0    Adaption to Adaptec CD268-01A Version 1.3
+		Version is PRE_ALPHA, unresolved points:
+		1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW
+		   thus driver causes CPU overhead and is very slow 
+		2. could not find a way to stop the drive, when it is
+		   in data read mode, therefore I had to set
+		   msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one
+		   frame can be read in sequence, this is also the reason for
+		3. getting 'timeout in state 4' messages, but nevertheless
+		   it works
+		W.Zimmermann, Oct. 31, 1994
+	V0.1    Version is ALPHA, problems #2 and #3 resolved.  
+		W.Zimmermann, Nov. 3, 1994
+	V0.2    Modification to some comments, debugging aids for partial test
+		with Borland C under DOS eliminated. Timer interrupt wait 
+		STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented; 
+		use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_
+		SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy 
+		waiting seems better to me than interrupt rescheduling.
+		Besides that, when used in the wrong place, STEN_LOW_WAIT causes
+		kernel panic.
+		In function aztPlay command ACMD_PLAY_AUDIO added, should make
+		audio functions work. The Aztech drive needs different commands
+		to read data tracks and play audio tracks.
+		W.Zimmermann, Nov. 8, 1994
+	V0.3    Recognition of missing drive during boot up improved (speeded up).
+		W.Zimmermann, Nov. 13, 1994
+	V0.35   Rewrote the control mechanism in azt_poll (formerly mcd_poll) 
+		including removal of all 'goto' commands. :-); 
+		J. Nardone, Nov. 14, 1994
+	V0.4    Renamed variables and constants to 'azt' instead of 'mcd'; had
+		to make some "compatibility" defines in azt.h; please note,
+		that the source file was renamed to azt.c, the include file to
+		azt.h                
+		Speeded up drive recognition during init (will be a little bit 
+		slower than before if no drive is installed!); suggested by
+		Robby Schirmer.
+		read_count declared volatile and set to AZT_BUF_SIZ to make
+		drive faster (now 300kB/sec, was 60kB/sec before, measured
+		by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096';
+		different AZT_BUF_SIZes were test, above 16 no further im-
+		provement seems to be possible; suggested by E.Moenkeberg.
+		W.Zimmermann, Nov. 18, 1994
+	V0.42   Included getAztStatus command in GetQChannelInfo() to allow
+		reading Q-channel info on audio disks, if drive is stopped, 
+		and some other bug fixes in the audio stuff, suggested by 
+		Robby Schirmer.
+		Added more ioctls (reading data in mode 1 and mode 2).
+		Completely removed the old azt_poll() routine.
+		Detection of ORCHID CDS-3110 in aztcd_init implemented.
+		Additional debugging aids (see the readme file).
+		W.Zimmermann, Dec. 9, 1994  
+	V0.50   Autodetection of drives implemented.
+		W.Zimmermann, Dec. 12, 1994
+	V0.52   Prepared for including in the standard kernel, renamed most
+		variables to contain 'azt', included autoconf.h
+		W.Zimmermann, Dec. 16, 1994        
+	V0.6    Version for being included in the standard Linux kernel.
+		Renamed source and header file to aztcd.c and aztcd.h
+		W.Zimmermann, Dec. 24, 1994
+	V0.7    Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case
+		CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl,
+		which causes kernel crashes when playing audio, changed 
+		include-files (config.h instead of autoconf.h, removed
+		delay.h)
+		W.Zimmermann, Jan. 8, 1995
+	V0.72   Some more modifications for adaption to the standard kernel.
+		W.Zimmermann, Jan. 16, 1995
+        V0.80   aztcd is now part of the standard kernel since version 1.1.83.
+                Modified the SET_TIMER and CLEAR_TIMER macros to comply with
+                the new timer scheme.
+                W.Zimmermann, Jan. 21, 1995
+        V0.90   Included CDROMVOLCTRL, but with my Aztech drive I can only turn
+                the channels on and off. If it works better with your drive, 
+                please mail me. Also implemented ACMD_CLOSE for CDROMSTART.
+                W.Zimmermann, Jan. 24, 1995
+        V1.00   Implemented close and lock tray commands. Patches supplied by
+		Frank Racis        
+                Added support for loadable MODULEs, so aztcd can now also be
+                loaded by insmod and removed by rmmod during run time
+                Werner Zimmermann, Mar. 24, 95
+        V1.10   Implemented soundcard configuration for Orchid CDS-3110 drives
+                connected to Soundwave32 cards. Release for LST 2.1.
+                (still experimental)
+                Werner Zimmermann, May 8, 95
+        V1.20   Implemented limited support for DOSEMU0.60's cdrom.c. Now it works, but
+                sometimes DOSEMU may hang for 30 seconds or so. A fully functional ver-
+                sion needs an update of Dosemu0.60's cdrom.c, which will come with the 
+                next revision of Dosemu.
+                Also Soundwave32 support now works.
+                Werner Zimmermann, May 22, 95
+	V1.30   Auto-eject feature. Inspired by Franc Racis (racis@psu.edu)
+	        Werner Zimmermann, July 4, 95
+	V1.40   Started multisession support. Implementation copied from mcdx.c
+	        by Heiko Schlittermann. Not tested yet.
+	        Werner Zimmermann, July 15, 95
+        V1.50   Implementation of ioctl CDROMRESET, continued multisession, began
+                XA, but still untested. Heavy modifications to drive status de-
+                tection.
+                Werner Zimmermann, July 25, 95
+        V1.60   XA support now should work. Speeded up drive recognition in cases, 
+                where no drive is installed.
+                Werner Zimmermann, August 8, 1995
+        V1.70   Multisession support now is completed, but there is still not 
+                enough testing done. If you can test it, please contact me. For
+                details please read README.aztcd.
+                Werner Zimmermann, August 19, 1995
+        V1.80   Modification to suit the new kernel boot procedure introduced
+                with kernel 1.3.33. Will definitely not work with older kernels.
+                Programming done by Linus himself.
+                Werner Zimmermann, October 11, 1995
+	NOTE: 
+	Points marked with ??? are questionable !
+*/
+#include <linux/major.h>
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+    char kernel_version[]= UTS_RELEASE;
+# endif
+#define aztcd_init init_module
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR AZTECH_CDROM_MAJOR 
+#include <linux/blk.h>
+
+#ifdef MODULE
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/aztcd.h>
+
+#define SET_TIMER(func, jifs)   delay_timer.expires = jiffies + (jifs); \
+                                delay_timer.function = (void *) (func); \
+                                add_timer(&delay_timer); 
+
+#define CLEAR_TIMER             del_timer(&delay_timer);
+
+#define RETURNM(message,value) {printk("aztcd: Warning: %s failed\n",message);\
+                                return value;}
+#define RETURN(message)        {printk("aztcd: Warning: %s failed\n",message);\
+                                return;}
+
+static int aztPresent = 0;
+
+#if 0
+#define AZT_TEST
+#define AZT_TEST1 /* <int-..> */
+#define AZT_TEST2 /* do_aztcd_request */
+#define AZT_TEST3 /* AZT_S_state */
+#define AZT_TEST4 /* QUICK_LOOP-counter */
+#define AZT_TEST5 /* port(1) state */
+#define AZT_DEBUG
+#define AZT_DEBUG_MULTISESSION
+#endif
+
+#define CURRENT_VALID \
+  (CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
+   && CURRENT -> sector != -1)
+
+#define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
+#define AZT_BUF_SIZ 16
+
+static volatile int azt_transfer_is_active=0;
+
+static char azt_buf[CD_FRAMESIZE_RAW*AZT_BUF_SIZ];/*buffer for block size conversion*/
+#if AZT_PRIVATE_IOCTLS
+static char buf[CD_FRAMESIZE_RAW];              /*separate buffer for the ioctls*/
+#endif
+
+static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn;
+static volatile int azt_buf_in, azt_buf_out = -1;
+static volatile int azt_error=0;
+static int azt_open_count=0;
+enum azt_state_e 
+{ AZT_S_IDLE,    /* 0 */
+  AZT_S_START,   /* 1 */
+  AZT_S_MODE,    /* 2 */
+  AZT_S_READ,    /* 3 */
+  AZT_S_DATA,    /* 4 */
+  AZT_S_STOP,    /* 5 */
+  AZT_S_STOPPING /* 6 */
+};
+static volatile enum azt_state_e azt_state = AZT_S_IDLE;
+#ifdef AZT_TEST3
+static volatile enum azt_state_e azt_state_old = AZT_S_STOP;  
+static volatile int azt_st_old = 0;
+#endif
+enum azt_read_modes 
+{ AZT_MODE_0,     /*read mode for audio disks, not supported by Aztech firmware*/
+  AZT_MODE_1,     /*read mode for normal CD-ROMs*/
+  AZT_MODE_2      /*read mode for XA CD-ROMs*/
+};
+static volatile enum azt_read_modes azt_read_mode = AZT_MODE_1;
+
+static int azt_mode = -1;
+static volatile int azt_read_count = 1;
+
+#define READ_TIMEOUT 3000
+
+#define azt_port aztcd  /*needed for the modutils*/
+static short azt_port = AZT_BASE_ADDR;
+
+static char  azt_cont = 0;
+static char  azt_init_end = 0;
+static char  azt_auto_eject = AZT_AUTO_EJECT;
+
+static int AztTimeout, AztTries;
+static struct wait_queue *azt_waitq = NULL; 
+static struct timer_list delay_timer = { NULL, NULL, 0, 0, NULL };
+
+static struct azt_DiskInfo DiskInfo;
+static struct azt_Toc Toc[MAX_TRACKS];
+static struct azt_Play_msf azt_Play;
+
+static int  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+static char aztDiskChanged = 1;
+static char aztTocUpToDate = 0;
+
+static void azt_transfer(void);
+static void azt_poll(void);
+static void azt_invalidate_buffers(void);
+static void do_aztcd_request(void);
+static void azt_hsg2msf(long hsg, struct msf *msf);
+static void azt_bin2bcd(unsigned char *p);
+static int  azt_bcd2bin(unsigned char bcd);
+static int  aztStatus(void);
+static int  getAztStatus(void);
+static int  aztSendCmd(int cmd);
+static int  sendAztCmd(int cmd, struct azt_Play_msf *params);
+static int  aztGetQChannelInfo(struct azt_Toc *qp);
+static int  aztUpdateToc(void);
+static int  aztGetDiskInfo(void);
+#if AZT_MULTISESSION 
+  static int  aztGetMultiDiskInfo(void);
+#endif
+static int  aztGetToc(int multi);
+static int  aztGetValue(unsigned char *result);
+static void aztStatTimer(void);
+static void aztCloseDoor(void);
+static void aztLockDoor(void);
+static void aztUnlockDoor(void);
+
+static unsigned char aztIndatum;
+static unsigned long aztTimeOutCount;
+static int aztCmd = 0;
+
+/* Macros for the drive hardware interface handshake, these macros use
+   busy waiting */
+/* Wait for OP_OK = drive answers with AFL_OP_OK after receiving a command*/
+# define OP_OK op_ok()
+void op_ok(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(DATA_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ printk("aztcd: Error Wait OP_OK\n");
+	  break;
+	}
+     } while (aztIndatum!=AFL_OP_OK);
+}
+
+/* Wait for PA_OK = drive answers with AFL_PA_OK after receiving parameters*/
+# define PA_OK pa_ok()
+void pa_ok(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(DATA_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ printk("aztcd: Error Wait PA_OK\n");
+	  break;
+	}
+     } while (aztIndatum!=AFL_PA_OK);
+}
+     
+/* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/
+# define STEN_LOW  sten_low()
+void sten_low(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(STATUS_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ if (azt_init_end) printk("aztcd: Error Wait STEN_LOW commands:%x\n",aztCmd);
+	  break;
+	}
+     } while (aztIndatum&AFL_STATUS);
+}
+
+/* Wait for DTEN=Low = handshake signal 'Data available'*/
+# define DTEN_LOW dten_low()
+void dten_low(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(STATUS_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ printk("aztcd: Error Wait DTEN_OK\n");
+	  break;
+	}
+     } while (aztIndatum&AFL_DATA);
+}
+
+/* 
+ * Macro for timer wait on STEN=Low, should only be used for 'slow' commands;
+ * may cause kernel panic when used in the wrong place
+*/
+#define STEN_LOW_WAIT   statusAzt()
+void statusAzt(void)
+{ AztTimeout = AZT_STATUS_DELAY;
+  SET_TIMER(aztStatTimer, HZ/100); 
+  sleep_on(&azt_waitq);
+  if (AztTimeout <= 0) printk("aztcd: Error Wait STEN_LOW_WAIT command:%x\n",aztCmd);
+  return;
+}
+
+static void aztStatTimer(void)
+{ if (!(inb(STATUS_PORT) & AFL_STATUS))
+     { wake_up(&azt_waitq);
+       return;
+     }
+  AztTimeout--;
+  if (AztTimeout <= 0)
+     { wake_up(&azt_waitq);
+       printk("aztcd: Error aztStatTimer: Timeout\n");
+       return;
+     }
+  SET_TIMER(aztStatTimer, HZ/100);
+}
+
+void aztcd_setup(char *str, int *ints)
+{  if (ints[0] > 0)
+      azt_port = ints[1];
+   if (ints[0] > 1)
+      azt_cont = ints[2];
+}
+
+/*
+ * Subroutines to automatically close the door (tray) and 
+ * lock it closed when the cd is mounted.  Leave the tray
+ * locking as an option
+ */
+static void aztCloseDoor(void)
+{
+  aztSendCmd(ACMD_CLOSE);
+  STEN_LOW;
+  return;
+}
+
+static void aztLockDoor(void)
+{
+#if AZT_ALLOW_TRAY_LOCK
+  aztSendCmd(ACMD_LOCK);
+  STEN_LOW;
+#endif
+  return;
+}
+
+static void aztUnlockDoor(void)
+{
+#if AZT_ALLOW_TRAY_LOCK
+  aztSendCmd(ACMD_UNLOCK);
+  STEN_LOW;
+#endif
+  return;
+}
+
+/* 
+ * Send a single command, return -1 on error, else 0
+*/
+static int aztSendCmd(int cmd)
+{  unsigned char data;
+   int retry;
+
+#ifdef AZT_DEBUG
+   printk("aztcd: Executing command %x\n",cmd);
+#endif
+   aztCmd=cmd;
+   outb(POLLED,MODE_PORT);
+   do { if (inb(STATUS_PORT)&AFL_STATUS) break;
+	inb(DATA_PORT);    /* if status left from last command, read and */
+      } while (1);         /* discard it */
+   do { if (inb(STATUS_PORT)&AFL_DATA) break;
+	inb(DATA_PORT);    /* if data left from last command, read and */
+      } while (1);         /* discard it */
+   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+     { outb((unsigned char) cmd,CMD_PORT);
+       STEN_LOW;
+       data=inb(DATA_PORT);
+       if (data==AFL_OP_OK)
+	 { return 0;}           /*OP_OK?*/
+       if (data==AFL_OP_ERR)
+	 { STEN_LOW;
+	   data=inb(DATA_PORT);
+	   printk("### Error 1 aztcd: aztSendCmd %x  Error Code %x\n",cmd,data);
+	 }
+     }
+   if (retry>=AZT_RETRY_ATTEMPTS)
+     { printk("### Error 2 aztcd: aztSendCmd %x \n",cmd);
+       azt_error=0xA5;
+     }
+   RETURNM("aztSendCmd",-1);
+}
+
+/*
+ * Send a play or read command to the drive, return -1 on error, else 0
+*/
+static int sendAztCmd(int cmd, struct azt_Play_msf *params)
+{  unsigned char data;
+   int retry;
+
+#ifdef AZT_DEBUG
+   printk("aztcd: play start=%02x:%02x:%02x  end=%02x:%02x:%02x\n", \
+	   params->start.min, params->start.sec, params->start.frame, \
+	   params->end.min,   params->end.sec,   params->end.frame);
+#endif   
+   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+     { aztSendCmd(cmd);
+       outb(params -> start.min,CMD_PORT);
+       outb(params -> start.sec,CMD_PORT);
+       outb(params -> start.frame,CMD_PORT);
+       outb(params -> end.min,CMD_PORT);
+       outb(params -> end.sec,CMD_PORT);
+       outb(params -> end.frame,CMD_PORT);
+       STEN_LOW;
+       data=inb(DATA_PORT);
+       if (data==AFL_PA_OK)
+	 { return 0;}           /*PA_OK ?*/
+       if (data==AFL_PA_ERR)
+	 { STEN_LOW;
+	   data=inb(DATA_PORT);
+	   printk("### Error 1 aztcd: sendAztCmd %x  Error Code %x\n",cmd,data);
+	 }
+     }
+   if (retry>=AZT_RETRY_ATTEMPTS)
+     { printk("### Error 2 aztcd: sendAztCmd %x\n ",cmd);
+       azt_error=0xA5;
+     }
+   RETURNM("sendAztCmd",-1);
+}
+
+/*
+ * Send a seek command to the drive, return -1 on error, else 0
+*/
+static int aztSeek(struct azt_Play_msf *params)
+{  unsigned char data;
+   int retry;
+
+#ifdef AZT_DEBUG
+   printk("aztcd: aztSeek %02x:%02x:%02x\n", \
+	   params->start.min, params->start.sec, params->start.frame);
+#endif   
+   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+     { aztSendCmd(ACMD_SEEK);
+       outb(params -> start.min,CMD_PORT);
+       outb(params -> start.sec,CMD_PORT);
+       outb(params -> start.frame,CMD_PORT);
+       STEN_LOW;
+       data=inb(DATA_PORT);
+       if (data==AFL_PA_OK)
+	 { return 0;}           /*PA_OK ?*/
+       if (data==AFL_PA_ERR)
+	 { STEN_LOW;
+	   data=inb(DATA_PORT);
+	   printk("### Error 1 aztcd: aztSeek\n");
+	 }
+     }
+   if (retry>=AZT_RETRY_ATTEMPTS)
+     { printk("### Error 2 aztcd: aztSeek\n ");
+       azt_error=0xA5;
+     }
+   RETURNM("aztSeek",-1);
+}
+
+/* Send a Set Disk Type command
+   does not seem to work with Aztech drives, behavior is completely indepen-
+   dent on which mode is set ???
+*/
+static int aztSetDiskType(int type)
+{  unsigned char data;
+   int retry;
+
+#ifdef AZT_DEBUG
+   printk("aztcd: set disk type command: type= %i\n",type);
+#endif
+   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+     { aztSendCmd(ACMD_SET_DISK_TYPE);
+       outb(type,CMD_PORT);
+       STEN_LOW;
+       data=inb(DATA_PORT);
+       if (data==AFL_PA_OK)     /*PA_OK ?*/
+	 { azt_read_mode=type;
+	   return 0;            
+	 }  
+       if (data==AFL_PA_ERR)
+	 { STEN_LOW;
+	   data=inb(DATA_PORT);
+	   printk("### Error 1 aztcd: aztSetDiskType %x Error Code %x\n",type,data);
+	 }
+     }
+   if (retry>=AZT_RETRY_ATTEMPTS)
+     { printk("### Error 2 aztcd: aztSetDiskType %x\n ",type);
+       azt_error=0xA5;
+     }
+   RETURNM("aztSetDiskType",-1);
+}
+
+
+/* 
+ * Checking if the media has been changed not yet implemented
+*/
+static int check_aztcd_media_change(kdev_t full_dev)
+{ return 0;
+}
+
+
+/* used in azt_poll to poll the status, expects another program to issue a 
+ * ACMD_GET_STATUS directly before 
+ */
+static int aztStatus(void)  
+{       int st;
+/*	int i;
+
+	i = inb(STATUS_PORT) & AFL_STATUS;    is STEN=0?    ???
+	if (!i)
+*/      STEN_LOW;
+        if (aztTimeOutCount<AZT_TIMEOUT)	
+	{   	st = inb(DATA_PORT) & 0xFF;
+		return st;
+	}
+	else
+		RETURNM("aztStatus",-1);
+}
+
+/*
+ * Get the drive status
+ */
+static int getAztStatus(void)
+{       int st;
+
+	if (aztSendCmd(ACMD_GET_STATUS)) RETURNM("getAztStatus 1",-1);
+	STEN_LOW;
+	st = inb(DATA_PORT) & 0xFF;
+#ifdef AZT_DEBUG
+	printk("aztcd: Status = %x\n",st);
+#endif
+	if ((st == 0xFF)||(st&AST_CMD_CHECK))
+	 { printk("aztcd: AST_CMD_CHECK error or no status available\n");
+	   return -1;
+	 }
+
+	if (((st&AST_MODE_BITS)!=AST_BUSY) && (aztAudioStatus == CDROM_AUDIO_PLAY))
+	   /* XXX might be an error? look at q-channel? */
+	   aztAudioStatus = CDROM_AUDIO_COMPLETED;
+
+	if ((st & AST_DSK_CHG)||(st & AST_NOT_READY))
+	 { aztDiskChanged = 1;
+	   aztTocUpToDate = 0;
+	   aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+	 }
+	return st;
+}
+
+
+/*
+ * Send a 'Play' command and get the status.  Use only from the top half.
+ */
+static int aztPlay(struct azt_Play_msf *arg)
+{       if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) RETURNM("aztPlay",-1);
+	return 0;
+}
+
+
+long azt_msf2hsg(struct msf *mp)
+{ return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75
+		                  + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET;
+}
+
+static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{       int i, st;
+	struct azt_Toc qInfo;
+	struct cdrom_ti ti;
+	struct cdrom_tochdr tocHdr;
+	struct cdrom_msf msf;
+	struct cdrom_tocentry entry;
+	struct azt_Toc *tocPtr;            
+	struct cdrom_subchnl subchnl;
+        struct cdrom_volctrl volctrl;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztcd_ioctl - Command:%x   Time: %li\n",cmd, jiffies);
+        printk("aztcd Status %x\n", getAztStatus());
+#endif
+	if (!ip) RETURNM("aztcd_ioctl 1",-EINVAL);
+	if (getAztStatus()<0) RETURNM("aztcd_ioctl 2", -EIO);
+	if ((!aztTocUpToDate)||(aztDiskChanged))
+	{ if ((i=aztUpdateToc())<0) RETURNM("aztcd_ioctl 3", i); /* error reading TOC */
+	}
+
+	switch (cmd)
+	{
+	case CDROMSTART:     /* Spin up the drive. Don't know, what to do,
+	                        at least close the tray */
+#if AZT_PRIVATE_IOCTLS 
+	        if (aztSendCmd(ACMD_CLOSE)) RETURNM("aztcd_ioctl 4",-1);
+	        STEN_LOW_WAIT;
+#endif
+		break;
+	case CDROMSTOP:      /* Spin down the drive */
+		if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 5",-1);
+		STEN_LOW_WAIT;
+		/* should we do anything if it fails? */
+		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMPAUSE:     /* Pause the drive */
+                if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL; 
+
+		if (aztGetQChannelInfo(&qInfo) < 0)
+		{ /* didn't get q channel info */
+		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		  RETURNM("aztcd_ioctl 7",0);
+		}
+		azt_Play.start = qInfo.diskTime;        /* remember restart point */
+
+		if (aztSendCmd(ACMD_PAUSE)) RETURNM("aztcd_ioctl 8",-1);
+		STEN_LOW_WAIT;
+		aztAudioStatus = CDROM_AUDIO_PAUSED;
+		break;
+	case CDROMRESUME:    /* Play it again, Sam */
+		if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL;
+		/* restart the drive at the saved position. */
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMMULTISESSION: /*multisession support -- experimental*/
+	        { struct cdrom_multisession ms;
+#ifdef AZT_DEBUG
+   		  printk("aztcd ioctl MULTISESSION\n");
+#endif
+		  st = verify_area(VERIFY_READ, (void*) arg, sizeof(struct cdrom_multisession));
+		  if (st) return st;
+		  memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession));
+		  if (ms.addr_format == CDROM_MSF) 
+		     { ms.addr.msf.minute = azt_bcd2bin(DiskInfo.lastSession.min);
+		       ms.addr.msf.second = azt_bcd2bin(DiskInfo.lastSession.sec);
+		       ms.addr.msf.frame  = azt_bcd2bin(DiskInfo.lastSession.frame);
+		     } 
+		  else if (ms.addr_format == CDROM_LBA)
+		       ms.addr.lba = azt_msf2hsg(&DiskInfo.lastSession);
+		  else
+		       return -EINVAL;
+		  ms.xa_flag = DiskInfo.xa;
+ 		  st = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct cdrom_multisession));
+		  if (st) return st;
+	  	  memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
+#ifdef AZT_DEBUG 
+ 		  if (ms.addr_format == CDROM_MSF) 
+                      printk("aztcd multisession xa:%d, msf:%02x:%02x.%02x [%02x:%02x.%02x])\n",
+			      ms.xa_flag, ms.addr.msf.minute, ms.addr.msf.second, 
+			      ms.addr.msf.frame, DiskInfo.lastSession.min,
+			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
+		  else
+		      printk("atzcd multisession %d, lba:0x%08x [%02x:%02x.%02x])\n",
+			      ms.xa_flag, ms.addr.lba, DiskInfo.lastSession.min,
+			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
+#endif
+   		  return 0;
+		}
+	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
+		if (st) return st;
+		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+		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;
+		azt_Play.start = Toc[ti.cdti_trk0].diskTime;
+		azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+	azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+	azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
+/*              if (aztAudioStatus == CDROM_AUDIO_PLAY) 
+		{ if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 9",-1);
+		  STEN_LOW;
+		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		}
+*/
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+		if (st) return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		/* convert to bcd */
+		azt_bin2bcd(&msf.cdmsf_min0);
+		azt_bin2bcd(&msf.cdmsf_sec0);
+		azt_bin2bcd(&msf.cdmsf_frame0);
+		azt_bin2bcd(&msf.cdmsf_min1);
+		azt_bin2bcd(&msf.cdmsf_sec1);
+		azt_bin2bcd(&msf.cdmsf_frame1);
+		azt_Play.start.min = msf.cdmsf_min0;
+		azt_Play.start.sec = msf.cdmsf_sec0;
+		azt_Play.start.frame = msf.cdmsf_frame0;
+		azt_Play.end.min = msf.cdmsf_min1;
+		azt_Play.end.sec = msf.cdmsf_sec1;
+		azt_Play.end.frame = msf.cdmsf_frame1;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+
+	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;
+		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+		break;
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof entry);
+		if (st) return st;
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
+		if (st) return st;
+		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+		if ((!aztTocUpToDate)||aztDiskChanged) aztUpdateToc();
+		if (entry.cdte_track == CDROM_LEADOUT)
+		  tocPtr = &Toc[DiskInfo.last + 1];   /* ??? */
+		else if (entry.cdte_track > DiskInfo.last
+				|| entry.cdte_track < DiskInfo.first)
+		{ return -EINVAL;
+		}
+		else 
+		  tocPtr = &Toc[entry.cdte_track];
+		entry.cdte_adr = tocPtr -> ctrl_addr;
+		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+		if (entry.cdte_format == CDROM_LBA)
+		  entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime);
+		else if (entry.cdte_format == CDROM_MSF)
+		{ entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min);
+		  entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec);
+		  entry.cdte_addr.msf.frame  = azt_bcd2bin(tocPtr -> diskTime.frame);
+		}
+		else
+		{ return -EINVAL;
+		}
+		memcpy_tofs((void *) arg, &entry, sizeof entry);
+		break;
+	case CDROMSUBCHNL:   /* Get subchannel info */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_subchnl));
+		if (st) { 
+#ifdef AZT_DEBUG
+		          printk("aztcd: exiting aztcd_ioctl - Error 1 - Command:%x\n",cmd);
+#endif
+		          return st;
+		        }  
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
+		if (st) { 
+#ifdef AZT_DEBUG
+		          printk("aztcd: exiting aztcd_ioctl - Error 2 - Command:%x\n",cmd);
+#endif
+		          return st;
+		        }  
+		memcpy_fromfs(&subchnl, (void *) arg, sizeof (struct cdrom_subchnl));
+		if (aztGetQChannelInfo(&qInfo) < 0)
+		if (st) { 
+#ifdef AZT_DEBUG
+		          printk("aztcd: exiting aztcd_ioctl - Error 3 - Command:%x\n",cmd);
+#endif
+     		  	  return -EIO;
+     		  	}  
+		subchnl.cdsc_audiostatus = aztAudioStatus;
+		subchnl.cdsc_adr = qInfo.ctrl_addr;
+		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
+		subchnl.cdsc_trk = azt_bcd2bin(qInfo.track);
+		subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex);
+		if (subchnl.cdsc_format == CDROM_LBA)
+		{ subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime);
+		  subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime);
+		}
+		else  /*default*/
+		{ subchnl.cdsc_format = CDROM_MSF;
+		  subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min);
+		  subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec);
+		  subchnl.cdsc_absaddr.msf.frame  = azt_bcd2bin(qInfo.diskTime.frame);
+		  subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min);
+		  subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec);
+		  subchnl.cdsc_reladdr.msf.frame  = azt_bcd2bin(qInfo.trackTime.frame);
+		}
+		memcpy_tofs((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
+		break;
+	case CDROMVOLCTRL:   /* Volume control 
+	 * With my Aztech CD268-01A volume control does not work, I can only
+	   turn the channels on (any value !=0) or off (value==0). Maybe it
+           works better with your drive */
+                st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl));
+                if (st) return (st);
+                memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
+		azt_Play.start.min = 0x21;
+		azt_Play.start.sec = 0x84;
+		azt_Play.start.frame = volctrl.channel0;
+		azt_Play.end.min =     volctrl.channel1;
+		azt_Play.end.sec =     volctrl.channel2;
+		azt_Play.end.frame =   volctrl.channel3;
+                sendAztCmd(ACMD_SET_VOLUME, &azt_Play);
+                STEN_LOW_WAIT;
+                break;
+	case CDROMEJECT:
+                aztUnlockDoor(); /* Assume user knows what they're doing */
+	       /* all drives can at least stop! */
+		if (aztAudioStatus == CDROM_AUDIO_PLAY) 
+		{ if (aztSendCmd(ACMD_STOP)) RETURNM("azt_ioctl 10",-1);
+		  STEN_LOW_WAIT;
+		}
+		if (aztSendCmd(ACMD_EJECT)) RETURNM("azt_ioctl 11",-1);
+		STEN_LOW_WAIT; /*???*/
+		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMEJECT_SW:
+		azt_auto_eject = (char) arg;
+		break;	
+        case CDROMRESET:
+        	outb(ACMD_SOFT_RESET,CMD_PORT);   /*send reset*/
+	        STEN_LOW;
+	        if (inb(DATA_PORT)!=AFL_OP_OK)    /*OP_OK?*/
+	          { printk("aztcd: AZTECH CD-ROM drive does not respond\n");
+                  }
+                break;
+/*Take care, the following code is not compatible with other CD-ROM drivers,
+  use it at your own risk with cdplay.c. Set AZT_PRIVATE_IOCTLS to 0 in aztcd.h,
+  if you do not want to use it!
+*/                  
+#if AZT_PRIVATE_IOCTLS 
+	case CDROMREADCOOKED: /*read data in mode 1 (2048 Bytes)*/
+	case CDROMREADRAW:    /*read data in mode 2 (2336 Bytes)*/
+		{ st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
+		  if (st) return st;
+		  st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf);
+		  if (st) return st;
+		  memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		  /* convert to bcd */
+		  azt_bin2bcd(&msf.cdmsf_min0);
+		  azt_bin2bcd(&msf.cdmsf_sec0);
+		  azt_bin2bcd(&msf.cdmsf_frame0);
+		  msf.cdmsf_min1=0;
+		  msf.cdmsf_sec1=0;
+		  msf.cdmsf_frame1=1; /*read only one frame*/
+		  azt_Play.start.min = msf.cdmsf_min0;
+		  azt_Play.start.sec = msf.cdmsf_sec0;
+		  azt_Play.start.frame = msf.cdmsf_frame0;
+		  azt_Play.end.min = msf.cdmsf_min1;
+		  azt_Play.end.sec = msf.cdmsf_sec1;
+		  azt_Play.end.frame = msf.cdmsf_frame1;
+		  if (cmd==CDROMREADRAW)
+		  { if (DiskInfo.xa)
+		       { return -1;         /*XA Disks can't be read raw*/
+		       }
+		    else   
+		       { if (sendAztCmd(ACMD_PLAY_READ_RAW, &azt_Play)) return -1;
+		         DTEN_LOW;
+		         insb(DATA_PORT,buf,CD_FRAMESIZE_RAW);
+		         memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE_RAW);
+		       }  
+		  }
+		  else /*CDROMREADCOOKED*/
+		  { if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
+		    DTEN_LOW;
+		    insb(DATA_PORT,buf,CD_FRAMESIZE);
+		    memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE);
+		  }
+		 } 
+                break;
+	case CDROMSEEK:    /*seek msf address*/
+		st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
+		if (st) return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		/* convert to bcd */
+		azt_bin2bcd(&msf.cdmsf_min0);
+		azt_bin2bcd(&msf.cdmsf_sec0);
+		azt_bin2bcd(&msf.cdmsf_frame0);
+		azt_Play.start.min = msf.cdmsf_min0;
+		azt_Play.start.sec = msf.cdmsf_sec0;
+		azt_Play.start.frame = msf.cdmsf_frame0;
+		if (aztSeek(&azt_Play)) return -1;
+                break;
+#endif /*end of incompatible code*/       
+	case CDROMREADMODE1: /*set read data in mode 1*/
+                return aztSetDiskType(AZT_MODE_1);
+	case CDROMREADMODE2: /*set read data in mode 2*/
+                return aztSetDiskType(AZT_MODE_2);	    
+	default:
+		return -EINVAL;
+	}
+#ifdef AZT_DEBUG
+        printk("aztcd: exiting aztcd_ioctl Command:%x  Time:%li\n",cmd,jiffies);
+#endif
+	return 0;
+}
+
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+static void azt_transfer(void)
+{ 
+#ifdef AZT_TEST
+  printk("aztcd: executing azt_transfer Time:%li\n",jiffies);
+#endif
+  if (CURRENT_VALID) {
+    while (CURRENT -> nr_sectors) {
+      int bn = CURRENT -> sector / 4;
+      int i;
+      for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i)
+	;
+      if (i < AZT_BUF_SIZ) {
+	int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+	int nr_sectors = 4 - (CURRENT -> sector & 3);
+	if (azt_buf_out != i) {
+	  azt_buf_out = i;
+	  if (azt_buf_bn[i] != bn) {
+	    azt_buf_out = -1;
+	    continue;
+	  }
+	}
+	if (nr_sectors > CURRENT -> nr_sectors)
+	  nr_sectors = CURRENT -> nr_sectors;
+	memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512);
+	CURRENT -> nr_sectors -= nr_sectors;
+	CURRENT -> sector += nr_sectors;
+	CURRENT -> buffer += nr_sectors * 512;
+      } else {
+	azt_buf_out = -1;
+	break;
+      }
+    }
+  }
+}
+
+
+static void do_aztcd_request(void)
+{
+#ifdef AZT_TEST
+  printk(" do_aztcd_request(%ld+%ld) Time:%li\n", CURRENT -> sector, CURRENT -> nr_sectors,jiffies);
+#endif
+  if (DiskInfo.audio) 
+    { printk("aztcd: Error, tried to mount an Audio CD\n");
+      end_request(0);
+      return;
+    }
+  azt_transfer_is_active = 1;
+  while (CURRENT_VALID) {
+    if (CURRENT->bh) {
+      if (!CURRENT->bh->b_lock)
+	panic(DEVICE_NAME ": block not locked");
+    }
+    azt_transfer();
+    if (CURRENT -> nr_sectors == 0) {
+      end_request(1);
+    } else {
+      azt_buf_out = -1;         /* Want to read a block not in buffer */
+      if (azt_state == AZT_S_IDLE) {
+	if ((!aztTocUpToDate)||aztDiskChanged) {
+	  if (aztUpdateToc() < 0) {
+	    while (CURRENT_VALID)
+	      end_request(0);
+	    break;
+	  }
+	}
+	azt_state = AZT_S_START;
+	AztTries = 5;
+	SET_TIMER(azt_poll, HZ/100);
+      }
+      break;
+    }
+  }
+  azt_transfer_is_active = 0;
+#ifdef AZT_TEST2
+  printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
+	  azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+  printk(" do_aztcd_request ends  Time:%li\n",jiffies);
+#endif
+}
+
+static void azt_poll(void)
+{
+    int st = 0;
+    int loop_ctl = 1;
+    int skip = 0;
+
+    if (azt_error) {                             /* ???*/
+	if (aztSendCmd(ACMD_GET_ERROR)) RETURN("azt_poll 1");
+	STEN_LOW;
+	azt_error=inb(DATA_PORT)&0xFF;
+	printk("aztcd: I/O error 0x%02x\n", azt_error);
+	azt_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+	if (AztTries == 5)
+	  printk("aztcd: Read of Block %d Failed - Maybe Audio Disk?\n", azt_next_bn);
+#endif
+	if (!AztTries--) {
+	  printk("aztcd: Read of Block %d Failed, Maybe Audio Disk? Giving up\n", azt_next_bn);
+	  if (azt_transfer_is_active) {
+	    AztTries = 0;
+	    loop_ctl = 0;
+	  }
+	  if (CURRENT_VALID)
+	    end_request(0);
+	  AztTries = 5;
+	}
+    azt_error = 0;
+    azt_state = AZT_S_STOP;
+    }
+
+    while (loop_ctl)
+    {
+      loop_ctl = 0;   /* each case must flip this back to 1 if we want
+			 to come back up here */
+      switch (azt_state) {
+
+	case AZT_S_IDLE:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_IDLE\n");
+	    }
+#endif
+	  return;
+
+	case AZT_S_START:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_START\n");
+	  }
+#endif
+	  if(aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 2");  /*result will be checked by aztStatus() */
+	  azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE;
+	  AztTimeout = 3000;
+	  break;
+
+	case AZT_S_MODE:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_MODE\n");
+	  }
+#endif
+	  if (!skip) {
+	    if ((st = aztStatus()) != -1) {
+	      if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
+		aztDiskChanged = 1;
+		aztTocUpToDate = 0;
+		azt_invalidate_buffers();
+		end_request(0);
+		printk("aztcd: Disk Changed or Not Ready 1 - Unmount Disk!\n");
+	      }
+	    } else break;
+	  }
+	  skip = 0;
+
+	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+            printk("aztcd: Disk Changed or Not Ready 2 - Unmount Disk!\n");
+            end_request(0);
+	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+	    if (azt_transfer_is_active) {
+	      azt_state = AZT_S_START;
+	      loop_ctl = 1;   /* goto immediately */
+	      break;
+	    }
+	    azt_state = AZT_S_IDLE;
+	    while (CURRENT_VALID)
+	      end_request(0);
+	    return;
+	  }
+					/*???*/
+/*	  if (aztSendCmd(ACMD_SET_MODE)) RETURN("azt_poll 3");
+	  outb(0x01, DATA_PORT);          
+	  PA_OK;
+	  STEN_LOW;
+*/        if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 4");
+	  STEN_LOW; /*???*/
+	  azt_mode = 1;
+	  azt_state = AZT_S_READ;
+	  AztTimeout = 3000;
+
+	  break;
+
+
+	case AZT_S_READ:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old)  {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_READ\n");
+	  }
+#endif
+	  if (!skip) {
+	      if ((st = aztStatus()) != -1) {
+		if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
+		aztDiskChanged = 1;
+		aztTocUpToDate = 0;
+		azt_invalidate_buffers();
+		printk("aztcd: Disk Changed or Not Ready 3 - Unmount Disk!\n");
+                end_request(0);               
+		}
+	      } else break;
+	  } 
+	    
+	  skip = 0;
+	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+	    if (azt_transfer_is_active) {
+	      azt_state = AZT_S_START;
+	      loop_ctl = 1;
+	      break;
+	    }
+	    azt_state = AZT_S_IDLE;
+	    while (CURRENT_VALID)
+	    end_request(0);
+	    return;
+	  }
+
+	  if (CURRENT_VALID) {
+	    struct azt_Play_msf msf;
+	    azt_next_bn = CURRENT -> sector / 4;
+	    azt_hsg2msf(azt_next_bn, &msf.start);
+	    azt_read_count=AZT_BUF_SIZ;    /*??? fast, because we read ahead*/
+/*          azt_read_count= CURRENT->nr_sectors;      slow
+*/
+	    msf.end.min = 0;
+	    msf.end.sec = 0;            
+	    msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
+#ifdef AZT_TEST3
+	    printk("---reading msf-address %x:%x:%x  %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame);
+	    printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
+		    azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+#endif 
+            if (azt_read_mode==AZT_MODE_2)
+               { sendAztCmd(ACMD_PLAY_READ_RAW, &msf); /*XA disks in raw mode*/
+               }
+            else
+               { sendAztCmd(ACMD_PLAY_READ, &msf);     /*others in cooked mode*/
+               }
+	    azt_state = AZT_S_DATA;
+	    AztTimeout = READ_TIMEOUT;
+	  } else {
+	    azt_state = AZT_S_STOP;
+	    loop_ctl = 1;
+	    break;
+	  }
+
+	  break;
+
+
+	case AZT_S_DATA:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old)  {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_DATA\n");
+	  }
+#endif
+
+	  st = inb(STATUS_PORT) & AFL_STATUSorDATA;   /*???*/
+
+	  switch (st) {
+
+	    case AFL_DATA:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st; 
+		printk("---AFL_DATA st:%x\n",st);
+	      }
+#endif
+	      if (!AztTries--) {
+		printk("aztcd: Read of Block %d Failed, Maybe Audio Disk ? Giving up\n", azt_next_bn);
+		if (azt_transfer_is_active) {
+		  AztTries = 0;
+		  break;
+		}
+		if (CURRENT_VALID)
+		  end_request(0);
+		AztTries = 5;
+	      }
+	      azt_state = AZT_S_START;
+	      AztTimeout = READ_TIMEOUT;
+	      loop_ctl = 1;
+	      break;
+
+	    case AFL_STATUSorDATA:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st;
+		printk("---AFL_STATUSorDATA st:%x\n",st);
+	      }
+#endif
+	      break;
+
+	    default:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st;
+		printk("---default: st:%x\n",st);
+	      }
+#endif
+	      AztTries = 5;
+	      if (!CURRENT_VALID && azt_buf_in == azt_buf_out) {
+		azt_state = AZT_S_STOP;
+		loop_ctl = 1;
+		break;
+	      }
+	      if (azt_read_count<=0)
+		printk("aztcd: warning - try to read 0 frames\n");
+	      while (azt_read_count)      /*??? fast read ahead loop*/
+	       { azt_buf_bn[azt_buf_in] = -1;
+		 DTEN_LOW;                      /*??? unsolved problem, very
+						      seldom we get timeouts
+						      here, don't now the real
+						      reason. With my drive this
+						      sometimes also happens with
+						      Aztech's original driver under
+						      DOS. Is it a hardware bug? 
+						      I tried to recover from such
+						      situations here. Zimmermann*/
+		 if (aztTimeOutCount>=AZT_TIMEOUT) 
+		  { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in);
+		    printk("azt_transfer_is_active:%x\n",azt_transfer_is_active);
+		    azt_read_count=0;
+		    azt_state = AZT_S_STOP;
+		    loop_ctl = 1;
+		    end_request(1);  /*should we have here (1) or (0)? */
+		  }
+		 else
+		  { if (azt_read_mode==AZT_MODE_2)
+		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE_RAW * azt_buf_in, CD_FRAMESIZE_RAW);
+		       }
+		    else
+		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE * azt_buf_in, CD_FRAMESIZE);
+                       }
+		    azt_read_count--;
+#ifdef AZT_TEST3
+		    printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count);
+		    printk("azt_next_bn:%d  azt_buf_in:%d azt_buf_out:%d  azt_buf_bn:%d\n", \
+			 azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+#endif
+		    azt_buf_bn[azt_buf_in] = azt_next_bn++;
+		    if (azt_buf_out == -1)
+		      azt_buf_out = azt_buf_in;
+		    azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1;
+		  }
+	       }
+	      if (!azt_transfer_is_active) {
+		while (CURRENT_VALID) {
+		  azt_transfer();
+		  if (CURRENT -> nr_sectors == 0)
+		    end_request(1);
+		  else
+		    break;
+		}
+	      }
+
+	      if (CURRENT_VALID
+		&& (CURRENT -> sector / 4 < azt_next_bn ||
+		CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) {
+		azt_state = AZT_S_STOP;
+		loop_ctl = 1;
+		break;
+	      }
+	      AztTimeout = READ_TIMEOUT;   
+	      if (azt_read_count==0) {
+		azt_state = AZT_S_STOP;   /*???*/
+		loop_ctl = 1;
+		break;           
+	      } 
+	      break;
+	    }
+    break;
+
+
+	case AZT_S_STOP:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_STOP\n");
+	  }
+#endif
+	  if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count);  /*???*/
+	  while (azt_read_count!=0) {
+	    int i;
+	    if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
+	      if (azt_read_mode==AZT_MODE_2)
+	         for (i=0; i<CD_FRAMESIZE_RAW; i++) inb(DATA_PORT);
+	      else   
+	         for (i=0; i<CD_FRAMESIZE; i++) inb(DATA_PORT);
+	    }
+	    azt_read_count--;
+	  }  
+	  if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 5");
+	  azt_state = AZT_S_STOPPING;
+	  AztTimeout = 1000;
+	  break;
+
+	case AZT_S_STOPPING:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_STOPPING\n");
+	  }
+#endif
+
+	  if ((st = aztStatus()) == -1 && AztTimeout)
+	    break;
+
+	  if ((st != -1) && ((st & AST_DSK_CHG)||(st & AST_NOT_READY))) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+	    azt_invalidate_buffers();
+	    printk("aztcd: Disk Changed or Not Ready 4 - Unmount Disk!\n");
+	    end_request(0);
+	  }
+
+
+#ifdef AZT_TEST3
+	  printk("CURRENT_VALID %d azt_mode %d\n",
+	     CURRENT_VALID, azt_mode);
+#endif
+
+	  if (CURRENT_VALID) {
+	    if (st != -1) {
+	      if (azt_mode == 1) {
+		azt_state = AZT_S_READ;
+		loop_ctl = 1;
+		skip = 1;
+		break;
+	      } else {
+		azt_state = AZT_S_MODE;
+		loop_ctl = 1;
+		skip = 1;
+		break;
+	      }
+	    } else {
+	      azt_state = AZT_S_START;
+	      AztTimeout = 1;
+	    }
+	  } else {
+	    azt_state = AZT_S_IDLE;
+	    return;
+	  }
+	  break;
+
+	default:
+	  printk("aztcd: invalid state %d\n", azt_state);
+	  return;
+      }  /* case */
+    } /* while */
+  
+
+   if (!AztTimeout--) 
+    { printk("aztcd: timeout in state %d\n", azt_state);
+      azt_state = AZT_S_STOP;
+      if (aztSendCmd(ACMD_STOP)) RETURN("azt_poll 6"); 
+      STEN_LOW_WAIT;     
+    };
+
+  SET_TIMER(azt_poll, HZ/100);
+}
+
+static void azt_invalidate_buffers(void)
+{ int i;
+
+#ifdef AZT_DEBUG
+  printk("aztcd: executing azt_invalidate_buffers\n");
+#endif
+  for (i = 0; i < AZT_BUF_SIZ; ++i)
+    azt_buf_bn[i] = -1;
+  azt_buf_out = -1;
+}
+
+/*
+ * Open the device special file.  Check that a disk is in.
+ */
+int aztcd_open(struct inode *ip, struct file *fp)
+{       int st;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztcd_open\n");
+#endif
+	if (aztPresent == 0)
+		return -ENXIO;                  /* no hardware */
+
+	if (!azt_open_count && azt_state == AZT_S_IDLE) 
+	  { azt_invalidate_buffers();
+
+	    st = getAztStatus();                    /* check drive status */
+	    if (st == -1) return -EIO;              /* drive doesn't respond */
+
+            if (st & AST_DOOR_OPEN)
+               { /* close door, then get the status again. */
+	         printk("aztcd: Door Open?\n");
+                 aztCloseDoor();     
+                 st = getAztStatus();
+               }        
+
+	    if ((st & AST_NOT_READY) || (st & AST_DSK_CHG)) /*no disk in drive or changed*/
+	       { printk("aztcd: Disk Changed or No Disk in Drive?\n");
+                 aztTocUpToDate=0;
+               }
+            if (aztUpdateToc())	return -EIO;
+	       
+	  }
+	++azt_open_count;
+        MOD_INC_USE_COUNT;
+	aztLockDoor();
+
+
+#ifdef AZT_DEBUG
+	printk("aztcd: exiting aztcd_open\n");
+#endif
+	return 0;
+}
+
+
+/*
+ * On close, we flush all azt blocks from the buffer cache.
+ */
+static void aztcd_release(struct inode * inode, struct file * file)
+{ 
+#ifdef AZT_DEBUG
+  printk("aztcd: executing aztcd_release\n");
+  printk("inode: %p, inode->i_rdev: %x    file: %p\n",inode,inode->i_rdev,file);
+#endif
+  MOD_DEC_USE_COUNT;
+  if (!--azt_open_count) {
+	azt_invalidate_buffers();
+	sync_dev(inode->i_rdev);             /*??? isn't it a read only dev?*/
+	invalidate_buffers(inode -> i_rdev);
+	aztUnlockDoor();
+        if (azt_auto_eject)
+           aztSendCmd(ACMD_EJECT);
+        CLEAR_TIMER;
+  }
+  return;
+}
+
+
+static struct file_operations azt_fops = {
+	NULL,                   /* lseek - default */
+	block_read,             /* read - general block-dev read */
+	block_write,            /* write - general block-dev write */
+	NULL,                   /* readdir - bad */
+	NULL,                   /* select */
+	aztcd_ioctl,            /* ioctl */
+	NULL,                   /* mmap */
+	aztcd_open,             /* open */
+	aztcd_release,          /* release */
+	NULL,                   /* fsync */
+	NULL,                   /* fasync*/
+	check_aztcd_media_change, /*media change*/
+	NULL                    /* revalidate*/
+};
+
+/*
+ * Test for presence of drive and initialize it.  Called at boot time.
+ */
+
+int aztcd_init(void)
+{       long int count, max_count;
+	unsigned char result[50];
+	int st;
+
+	if (azt_port <= 0) {
+	  printk("aztcd: no Aztech CD-ROM Initialization");
+          return -EIO;
+	}
+	printk("aztcd: Aztech, Orchid, Okano, Wearnes CD-ROM Driver (C) 1994,1995 W.Zimmermann\n");
+	printk("aztcd: DriverVersion=%s  BaseAddress=0x%x \n",AZT_VERSION,azt_port);
+
+	if (check_region(azt_port, 4)) {
+	  printk("aztcd: conflict, I/O port (%X) already used\n",
+		 azt_port);
+          return -EIO;
+	}
+
+#ifdef AZT_SW32   /*CDROM connected to Soundwave32 card*/
+        if ((0xFF00 & inw(AZT_SW32_ID_REG)) != 0x4500)
+           { printk("aztcd: no Soundwave32 card detected at base:%x init:%x config:%x id:%x\n",
+		 AZT_SW32_BASE_ADDR,AZT_SW32_INIT,AZT_SW32_CONFIG_REG,AZT_SW32_ID_REG);
+                 return -EIO;
+	   }
+        else                
+           { printk("aztcd: Soundwave32 card detected at %x  Version %x\n",
+	 	 AZT_SW32_BASE_ADDR, inw(AZT_SW32_ID_REG));
+	     outw(AZT_SW32_INIT,AZT_SW32_CONFIG_REG);
+	     for (count=0;count<10000;count++);          /*delay a bit*/    	 
+           }
+#endif	
+
+	/* check for presence of drive */
+	outb(POLLED,MODE_PORT);                 /*???*/
+	inb(CMD_PORT);
+	inb(CMD_PORT);
+	outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/
+
+/*	STEN_LOW  - special implementation for drive recognition
+*/      aztTimeOutCount=0;   
+	do { aztIndatum=inb(STATUS_PORT);
+	     aztTimeOutCount++; 
+	     if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; 
+	   } while (aztIndatum&AFL_STATUS); 
+
+	if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/
+           { 
+#ifndef MODULE
+             if (azt_cont!=0x79)   
+	        { printk("aztcd: no AZTECH CD-ROM drive found-Try boot parameter aztcd=<BaseAddress>,0x79\n");
+	          return -EIO;
+                }
+#else        
+             if (0)
+                {
+                }
+#endif	     
+             else   
+       	        { printk("aztcd: drive reset - please wait\n");
+	          for (count=0;count<50;count++)
+	            { inb(STATUS_PORT);    /*removing all data from earlier tries*/
+	              inb(DATA_PORT);
+	            }
+	          outb(POLLED,MODE_PORT);           /*???*/
+	          inb(CMD_PORT);
+	          inb(CMD_PORT);
+		  getAztStatus(); 		    /*trap errors*/
+	          outb(ACMD_SOFT_RESET,CMD_PORT);   /*send reset*/
+	          STEN_LOW;
+	          if (inb(DATA_PORT)!=AFL_OP_OK)    /*OP_OK?*/
+	             { printk("aztcd: no AZTECH CD-ROM drive found\n");
+                       return -EIO;
+	             } 
+	          for (count = 0; count < AZT_TIMEOUT; count++); 
+	             { count=count*2;          /* delay a bit */
+	               count=count/2;
+	             }                        
+	          if ((st=getAztStatus())==-1)
+	             { printk("aztcd: Drive Status Error Status=%x\n",st);
+                       return -EIO;
+	             }
+#ifdef AZT_DEBUG
+	          printk("aztcd: Status = %x\n",st);
+#endif
+	          outb(POLLED,MODE_PORT);              /*???*/
+	          inb(CMD_PORT);
+	          inb(CMD_PORT);
+	          outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/
+	          STEN_LOW;
+	          OP_OK;
+	        } 
+           }
+	azt_init_end=1;
+	STEN_LOW;
+	result[0]=inb(DATA_PORT);        /*reading in a null byte???*/
+	for (count=1;count<50;count++)   /*Reading version string*/
+	 { aztTimeOutCount=0;            /*here we must implement STEN_LOW differently*/
+	   do { aztIndatum=inb(STATUS_PORT);/*because we want to exit by timeout*/
+		aztTimeOutCount++; 
+		if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; 
+	      } while (aztIndatum&AFL_STATUS); 
+	   if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break;  /*all chars read?*/
+	   result[count]=inb(DATA_PORT);
+	 }
+	if (count>30) max_count=30;  /*print max.30 chars of the version string*/
+	else          max_count=count;
+	printk("aztcd: FirmwareVersion=");
+	for (count=1;count<max_count;count++) printk("%c",result[count]);
+	printk("<<<\n");
+
+	if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T'))
+	 { printk("aztcd: AZTECH drive detected\n"); /*AZTECH*/    
+	 }
+	else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D'))
+	 { printk("aztcd: ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
+	 }
+	else                                               /*OTHERS or none*/
+	 { printk("aztcd: : unknown drive or firmware version detected\n");
+	   printk("                      azt may not run stable, if you want to try anyhow,\n");
+	   printk("                      boot with: aztcd=<BaseAddress>,0x79\n");
+	   if ((azt_cont!=0x79))     
+	     { printk("aztcd: FirmwareVersion=");
+	       for (count=1;count<5;count++) printk("%c",result[count]);
+	       printk("\n");
+	       printk("aztcd: Aborted\n");
+               return -EIO;
+	     }
+	 }
+	if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0)
+	{
+		printk("aztcd: Unable to get major %d for Aztech CD-ROM\n",
+		       MAJOR_NR);
+                return -EIO;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+
+	request_region(azt_port, 4, "aztcd");
+
+	azt_invalidate_buffers();
+	aztPresent = 1;
+	aztCloseDoor();
+	printk("aztcd: End Init\n");
+        return (0);
+}
+
+
+static void azt_hsg2msf(long hsg, struct msf *msf)
+{       hsg += 150;
+	msf -> min = hsg / 4500;
+	hsg %= 4500;
+	msf -> sec = hsg / 75;
+	msf -> frame = hsg % 75;
+#ifdef AZT_DEBUG
+	if (msf->min  >=70) printk("aztcd: Error hsg2msf address Minutes\n");
+	if (msf->sec  >=60) printk("aztcd: Error hsg2msf address Seconds\n");
+	if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n");
+#endif
+	azt_bin2bcd(&msf -> min);           /* convert to BCD */
+	azt_bin2bcd(&msf -> sec);
+	azt_bin2bcd(&msf -> frame);
+}
+
+
+static void azt_bin2bcd(unsigned char *p)
+{       int u, t;
+
+	u = *p % 10;
+	t = *p / 10;
+	*p = u | (t << 4);
+}
+
+static int azt_bcd2bin(unsigned char bcd)
+{       return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+
+
+
+/*
+ * Read a value from the drive.  Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling. The read command itself must
+ * be issued with aztSendCmd() directly before
+ */
+static int aztGetValue(unsigned char *result)
+{       int s;
+
+	STEN_LOW;
+	if (aztTimeOutCount>=AZT_TIMEOUT)
+	{       printk("aztcd: aztGetValue timeout\n");
+		return -1;
+	}
+	s = inb(DATA_PORT) & 0xFF;
+	*result = (unsigned char) s;
+	return 0;
+}
+
+
+/*
+ * Read the current Q-channel info.  Also used for reading the
+ * table of contents.
+ */
+int aztGetQChannelInfo(struct azt_Toc *qp)
+{       unsigned char notUsed;
+	int st;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztGetQChannelInfo  Time:%li\n",jiffies);
+#endif
+	if ((st=getAztStatus())==-1)        RETURNM("aztGetQChannelInfo 1",-1);
+	if (aztSendCmd(ACMD_GET_Q_CHANNEL)) RETURNM("aztGetQChannelInfo 2",-1);
+	/*STEN_LOW_WAIT; ??? Dosemu0.60's cdrom.c does not like STEN_LOW_WAIT here*/
+	if (aztGetValue(&notUsed)) RETURNM("aztGetQChannelInfo 3",-1); /*??? Nullbyte einlesen*/
+	if ((st&AST_MODE_BITS)==AST_INITIAL)
+	 { qp->ctrl_addr=0;      /* when audio stop ACMD_GET_Q_CHANNEL returns */
+	   qp->track=0;          /* only one byte with Aztech drives */
+	   qp->pointIndex=0;
+	   qp->trackTime.min=0;
+	   qp->trackTime.sec=0;
+	   qp->trackTime.frame=0;
+	   qp->diskTime.min=0;
+	   qp->diskTime.sec=0;
+	   qp->diskTime.frame=0;
+	   return 0;  
+	 }
+	else
+	 { if (aztGetValue(&qp -> ctrl_addr) < 0)       RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> track) < 0)           RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> pointIndex) < 0)      RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> trackTime.min) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> trackTime.sec) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> trackTime.frame) < 0) RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&notUsed) < 0)               RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> diskTime.min) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> diskTime.sec) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> diskTime.frame) < 0)  RETURNM("aztGetQChannelInfo 4",-1);
+	 }
+#ifdef AZT_DEBUG
+	printk("aztcd: exiting aztGetQChannelInfo  Time:%li\n",jiffies);
+#endif
+	return 0;
+}
+
+/*
+ * Read the table of contents (TOC) and TOC header if necessary
+ */
+static int aztUpdateToc()
+{       int st;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztUpdateToc  Time:%li\n",jiffies);
+#endif  
+	if (aztTocUpToDate)
+		return 0;
+
+	if (aztGetDiskInfo() < 0)
+		return -EIO;
+
+	if (aztGetToc(0) < 0)
+		return -EIO;
+
+        /*audio disk detection
+          with my Aztech drive there is no audio status bit, so I use the copy
+          protection bit of the first track. If this track is copy protected 
+          (copy bit = 0), I assume, it's an audio  disk. Strange, but works ??? */
+        if (!(Toc[DiskInfo.first].ctrl_addr & 0x40)) 
+           DiskInfo.audio=1;
+        else 
+           DiskInfo.audio=0;
+
+        /* XA detection */
+	if (! DiskInfo.audio) 
+	   { azt_Play.start.min   = 0;  /*XA detection only seems to work*/
+             azt_Play.start.sec   = 2;  /*when we play a track*/
+	     azt_Play.start.frame = 0;
+	     azt_Play.end.min     = 0;
+	     azt_Play.end.sec     = 0;
+	     azt_Play.end.frame   = 1;
+	     if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
+	     DTEN_LOW;
+	     for (st=0;st<CD_FRAMESIZE;st++) inb(DATA_PORT);
+           } 
+        DiskInfo.xa = getAztStatus() & AST_MODE;
+        if (DiskInfo.xa) 
+           { printk("aztcd: XA support experimental - mail results to zimmerma@rz.fht-esslingen.de\n");
+           }
+        
+        /*multisession detection
+          support for multisession CDs is done automatically with Aztech drives,
+          we don't have to take care about TOC redirection; if we want the isofs
+          to take care about redirection, we have to set AZT_MULTISESSION to 1*/
+        DiskInfo.multi=0;
+#if AZT_MULTISESSION
+  	if (DiskInfo.xa) 
+  	   { aztGetMultiDiskInfo(); /*here Disk.Info.multi is set*/
+  	   }
+#endif
+        if (DiskInfo.multi)
+           { DiskInfo.lastSession.min  = Toc[DiskInfo.next].diskTime.min;
+	     DiskInfo.lastSession.sec  = Toc[DiskInfo.next].diskTime.sec;
+             DiskInfo.lastSession.frame= Toc[DiskInfo.next].diskTime.frame;
+             printk("aztcd: Multisession support experimental\n");
+           }
+        else
+           { DiskInfo.lastSession.min  = Toc[DiskInfo.first].diskTime.min;
+	     DiskInfo.lastSession.sec  = Toc[DiskInfo.first].diskTime.sec;
+             DiskInfo.lastSession.frame= Toc[DiskInfo.first].diskTime.frame;
+           }
+
+	aztTocUpToDate = 1;
+#ifdef AZT_DEBUG
+	printk("aztcd: exiting aztUpdateToc  Time:%li\n",jiffies);
+#endif
+	return 0;
+}
+
+
+/* Read the table of contents header, i.e. no. of tracks and start of first 
+ * track
+ */
+static int aztGetDiskInfo()
+{ int limit;
+  unsigned char test;
+  struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+  printk("aztcd: starting aztGetDiskInfo  Time:%li\n",jiffies);
+#endif
+  if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetDiskInfo 1",-1);
+  STEN_LOW_WAIT;
+  test=0;
+  for (limit=300;limit>0;limit--)
+   {  if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetDiskInfo 2",-1);
+      if (qInfo.pointIndex==0xA0)   /*Number of FirstTrack*/
+	{ DiskInfo.first = qInfo.diskTime.min;
+	  DiskInfo.first = azt_bcd2bin(DiskInfo.first);
+	  test=test|0x01;
+	}
+      if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
+	{ DiskInfo.last  = qInfo.diskTime.min;
+	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
+	  test=test|0x02;
+	}
+      if (qInfo.pointIndex==0xA2)   /*DiskLength*/
+	{ DiskInfo.diskLength.min=qInfo.diskTime.min;
+	  DiskInfo.diskLength.sec=qInfo.diskTime.sec;
+	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
+	  test=test|0x04;
+	}
+      if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01))   /*StartTime of First Track*/
+	{ DiskInfo.firstTrack.min=qInfo.diskTime.min;
+	  DiskInfo.firstTrack.sec=qInfo.diskTime.sec;
+	  DiskInfo.firstTrack.frame=qInfo.diskTime.frame;
+	  test=test|0x08;
+	}
+      if (test==0x0F) break;
+   }
+#ifdef AZT_DEBUG
+  printk ("aztcd: exiting aztGetDiskInfo  Time:%li\n",jiffies);
+  printk("Disk Info: first %d last %d length %02X:%02X.%02X dez  first %02X:%02X.%02X dez\n",
+  	  DiskInfo.first,
+	  DiskInfo.last,
+	  DiskInfo.diskLength.min,
+	  DiskInfo.diskLength.sec,
+	  DiskInfo.diskLength.frame,
+	  DiskInfo.firstTrack.min,
+	  DiskInfo.firstTrack.sec,
+	  DiskInfo.firstTrack.frame);
+#endif
+  if (test!=0x0F) return -1;
+  return 0;
+}
+
+#if AZT_MULTISESSION
+/*
+ * Get Multisession Disk Info
+ */
+static int aztGetMultiDiskInfo(void)
+{ int limit, k=5;
+  unsigned char test;
+  struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+  printk("aztcd: starting aztGetMultiDiskInfo\n");
+#endif
+
+  do { azt_Play.start.min   = Toc[DiskInfo.last+1].diskTime.min;
+       azt_Play.start.sec   = Toc[DiskInfo.last+1].diskTime.sec;
+       azt_Play.start.frame = Toc[DiskInfo.last+1].diskTime.frame;
+       test=0;
+
+       for (limit=30;limit>0;limit--)   /*Seek for LeadIn of next session*/
+           { if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 1",-1);
+             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 2",-1);
+             if ((qInfo.track==0)&&(qInfo.pointIndex)) break;  /*LeadIn found*/
+             if ((azt_Play.start.sec+=10) > 59)
+                { azt_Play.start.sec=0;
+                  azt_Play.start.min++;
+                }
+           }
+       if (!limit) break;  /*Check, if a leadin track was found, if not we're
+                             at the end of the disk*/
+#ifdef AZT_DEBUG_MULTISESSION
+       printk("leadin found track %d  pointIndex %x  limit %d\n",qInfo.track,qInfo.pointIndex,limit);
+#endif
+       for (limit=300;limit>0;limit--)
+           { if (++azt_Play.start.frame>74)
+                { azt_Play.start.frame=0;
+                  if (azt_Play.start.sec > 59)
+                     { azt_Play.start.sec=0;
+                       azt_Play.start.min++;
+                     }
+                }     
+             if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 3",-1);
+             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 4",-1);
+             if (qInfo.pointIndex==0xA0)   /*Number of NextTrack*/
+	        { DiskInfo.next = qInfo.diskTime.min;
+	          DiskInfo.next = azt_bcd2bin(DiskInfo.next);
+	          test=test|0x01;
+	        }
+             if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
+	 	{ DiskInfo.last  = qInfo.diskTime.min;
+	   	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
+	   	  test=test|0x02;
+	 	}
+      	     if (qInfo.pointIndex==0xA2)   /*DiskLength*/
+	 	{ DiskInfo.diskLength.min  =qInfo.diskTime.min;
+	   	  DiskInfo.diskLength.sec  =qInfo.diskTime.sec;
+	   	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
+	  	  test=test|0x04;
+	 	}
+      	     if ((qInfo.pointIndex==DiskInfo.next)&&(test&0x01))   /*StartTime of Next Track*/
+		{ DiskInfo.nextSession.min=qInfo.diskTime.min;
+	  	  DiskInfo.nextSession.sec=qInfo.diskTime.sec;
+	  	  DiskInfo.nextSession.frame=qInfo.diskTime.frame;
+	  	  test=test|0x08;
+		}
+      	     if (test==0x0F) break;
+   	   }
+#ifdef AZT_DEBUG_MULTISESSION
+       printk ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez  first %02x:%02x.%02x dez  next %02x:%02x.%02x dez\n",
+     		DiskInfo.first,
+		DiskInfo.next,
+		DiskInfo.last,
+		DiskInfo.diskLength.min,
+		DiskInfo.diskLength.sec,
+		DiskInfo.diskLength.frame,
+		DiskInfo.firstTrack.min,
+		DiskInfo.firstTrack.sec,
+		DiskInfo.firstTrack.frame,
+		DiskInfo.nextSession.min,
+		DiskInfo.nextSession.sec,
+		DiskInfo.nextSession.frame);
+#endif
+       if (test!=0x0F) 
+           break;
+       else 
+           DiskInfo.multi=1;   /*found TOC of more than one session*/
+       aztGetToc(1);
+     } while(--k);
+
+#ifdef AZT_DEBUG
+  printk ("aztcd: exiting aztGetMultiDiskInfo  Time:%li\n",jiffies);
+#endif
+  return 0;
+}
+#endif
+
+/*
+ * Read the table of contents (TOC)
+ */
+static int aztGetToc(int multi)
+{ int i, px;
+  int limit;
+  struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+  printk("aztcd: starting aztGetToc  Time:%li\n",jiffies);
+#endif
+  if (!multi)
+     { for (i = 0; i < MAX_TRACKS; i++)
+ 	    Toc[i].pointIndex = 0;
+       i = DiskInfo.last + 3;
+     }
+  else
+     { for (i = DiskInfo.next; i < MAX_TRACKS; i++)
+     	    Toc[i].pointIndex = 0; 
+       i = DiskInfo.last + 4 - DiskInfo.next;
+     }
+
+/*Is there a good reason to stop motor before TOC read?
+  if (aztSendCmd(ACMD_STOP)) RETURNM("aztGetToc 1",-1);
+      STEN_LOW_WAIT;
+*/
+
+  if (!multi)
+     { azt_mode = 0x05;
+       if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1); /*???*/
+       STEN_LOW_WAIT;
+     }
+  for (limit = 300; limit > 0; limit--)
+      { if (multi)
+           { if (++azt_Play.start.sec > 59)
+                { azt_Play.start.sec=0;
+                  azt_Play.start.min++;
+                }
+             if (aztSeek(&azt_Play)) RETURNM("aztGetToc 3",-1);
+           }
+	if (aztGetQChannelInfo(&qInfo) < 0)
+	    break;
+
+	px = azt_bcd2bin(qInfo.pointIndex);
+
+	if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+  	    if (Toc[px].pointIndex == 0)
+	       { Toc[px] = qInfo;
+		 i--;
+	       }
+
+	if (i <= 0)
+	    break;
+      }
+
+  Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+
+
+#ifdef AZT_DEBUG_MULTISESSION 
+  printk("aztcd: exiting aztGetToc\n");
+  for (i = 1; i <= DiskInfo.last+1; i++)
+       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
+               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+  for (i = 100; i < 103; i++)
+       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
+               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+#endif
+
+  return limit > 0 ? 0 : -1;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{ if (MOD_IN_USE)
+    { printk("aztcd module in use - can't remove it.\n");
+      return;
+    }
+  if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))    
+    { printk("What's that: can't unregister aztcd\n");
+      return;
+    }
+   release_region(azt_port,4);
+   printk("aztcd module released.\n");
+}   
+#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v1.3.35/linux/drivers/cdrom/cdu31a.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/cdu31a.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,3100 @@
+/*
+ * Sony CDU-31A CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h).  With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * WARNING - 	All autoprobes have been removed from the driver.
+ *		You MUST configure the CDU31A via a LILO config
+ *		at boot time or in lilo.conf.  I have the
+ *		following in my lilo.conf:
+ *
+ *                append="cdu31a=0x1f88,0,PAS"
+ *
+ *		The first number is the I/O base address of the
+ *		card.  The second is the interrupt (0 means none).
+ *		The third should be "PAS" if on a Pro-Audio
+ *		spectrum, or nothing if on something else.
+ *
+ * This interface is (unfortunately) a polled interface.  This is
+ * because most Sony interfaces are set up with DMA and interrupts
+ * disables.  Some (like mine) do not even have the capability to
+ * handle interrupts or DMA.  For this reason you will see a lot of
+ * the following:
+ *
+ *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+ *   while ((retry_count > jiffies) && (! <some condition to wait for))
+ *   {
+ *      while (handle_sony_cd_attention())
+ *         ;
+ *
+ *      sony_sleep();
+ *   }
+ *   if (the condition not met)
+ *   {
+ *      return an error;
+ *   }
+ *
+ * This ugly hack waits for something to happen, sleeping a little
+ * between every try.  it also handles attentions, which are
+ * asynchronous events from the drive informing the driver that a disk
+ * has been inserted, removed, etc.
+ *
+ * NEWS FLASH - The driver now supports interrupts but they are
+ * turned off by default.  Use of interrupts is highly encouraged, it
+ * cuts CPU usage down to a reasonable level.  I had DMA in for a while
+ * but PC DMA is just too slow.  Better to just insb() it.
+ *
+ * One thing about these drives: They talk in MSF (Minute Second Frame) format.
+ * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+ * disk.  The funny thing is that these are sent to the drive in BCD, but the
+ * interface wants to see them in decimal.  A lot of conversion goes on.
+ *
+ * DRIVER SPECIAL FEATURES
+ * -----------------------
+ *
+ * This section describes features beyond the normal audio and CD-ROM
+ * functions of the drive.
+ *
+ * 2048 byte buffer mode
+ *
+ * If a disk is mounted with -o block=2048, data is copied straight
+ * from the drive data port to the buffer.  Otherwise, the readahead
+ * buffer must be involved to hold the other 1K of data when a 1K
+ * block operation is done.  Note that with 2048 byte blocks you
+ * cannot execute files from the CD.
+ *
+ * XA compatibility
+ *
+ * The driver should support XA disks for both the CDU31A and CDU33A.
+ * It does this transparently, the using program doesn't need to set it.
+ *
+ * Multi-Session
+ *
+ * A multi-session disk looks just like a normal disk to the user.
+ * Just mount one normally, and all the data should be there.
+ * A special thanks to Koen for help with this!
+ * 
+ * Raw sector I/O
+ *
+ * Using the CDROMREADAUDIO it is possible to read raw audio and data
+ * tracks.  Both operations return 2352 bytes per sector.  On the data
+ * tracks, the first 12 bytes is not returned by the drive and the value
+ * of that data is indeterminate.
+ *
+ *
+ *  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.
+ *
+ */
+
+/*
+ *
+ * Setting up the Sony CDU31A/CDU33A drive interface card.  If
+ * You have another card, you are on your own.
+ * 
+ *      +----------+-----------------+----------------------+
+ *      |  JP1     |  34 Pin Conn    |                      |
+ *      |  JP2     +-----------------+                      |
+ *      |  JP3                                              |
+ *      |  JP4                                              |
+ *      |                                                   +--+
+ *      |                                                   |  +-+
+ *      |                                                   |  | |  External
+ *      |                                                   |  | |  Connector
+ *      |                                                   |  | |
+ *      |                                                   |  +-+
+ *      |                                                   +--+
+ *      |                                                   |
+ *      |                                          +--------+
+ *      |                                          |
+ *      +------------------------------------------+
+ * 
+ *    JP1 sets the Base Address, using the following settings:
+ * 
+ *      Address         Pin 1           Pin 2
+ *      -------         -----           -----
+ *      0x320           Short           Short
+ *      0x330           Short           Open
+ *      0x340           Open            Short
+ *      0x360           Open            Open
+ * 
+ *    JP2 and JP3 configure the DMA channel; they must be set the same.
+ * 
+ *      DMA             Pin 1           Pin 2           Pin 3
+ *      ---             -----           -----           -----
+ *      1               On              Off             On
+ *      2               Off             On              Off
+ *      3               Off             Off             On
+ * 
+ *    JP4 Configures the IRQ:
+ * 
+ *      IRQ     Pin 1           Pin 2           Pin 3           Pin 4
+ *      ---     -----           -----           -----           -----
+ *      3       Off             Off             On              Off
+ *      4       Off             Off*            Off             On
+ *      5       On              Off             Off             Off
+ *      6       Off             On              Off             Off
+ * 
+ *              * The documentation states to set this for interrupt
+ *                4, but I think that is a mistake.
+ */
+
+#include <linux/major.h>
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+char kernel_version[]= UTS_RELEASE;
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/dma.h>
+
+#include <linux/cdrom.h>
+#include <linux/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_MAX_CONSECUTIVE_ATTENTIONS 10
+
+/* Define the following if you have data corruption problems. */
+#undef SONY_POLL_EACH_BYTE
+
+/*
+** Edit the following data to change interrupts, DMA channels, etc.
+** Default is polled and no DMA.  DMA is not recommended for double-speed
+** drives.
+*/
+static struct
+{
+   unsigned short base;         /* I/O Base Address */
+   short          int_num;      /* Interrupt Number (-1 means scan for it,
+                                   0 means don't use) */
+} cdu31a_addresses[] =
+{
+#if 0	/* No autoconfig any more. See Note at beginning
+	   of this file. */
+   { 0x340,     0 },    /* Standard configuration Sony Interface */
+   { 0x1f88,    0 },    /* Fusion CD-16 */
+   { 0x230,     0 },    /* SoundBlaster 16 card */
+   { 0x360,     0 },    /* Secondary standard Sony Interface */
+   { 0x320,     0 },    /* Secondary standard Sony Interface */
+   { 0x330,     0 },    /* Secondary standard Sony Interface */
+   { 0x634,     0 },    /* Sound FX SC400 */
+   { 0x654,     0 },    /* Sound FX SC400 */
+#endif
+   { 0 }
+};
+
+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 void do_sony_cd_cmd(unsigned char cmd,
+                           unsigned char *params,
+                           unsigned int num_params,
+                           unsigned char *result_buffer,
+                           unsigned int *result_size);
+static void size_to_buf(unsigned int size,
+                        unsigned char *buf);
+
+/* Parameters for the read-ahead. */
+static unsigned int sony_next_block;      /* Next 512 byte block offset */
+static unsigned int sony_blocks_left = 0; /* Number of 512 byte blocks left
+                                             in the current read command. */
+
+
+/* The base I/O address of the Sony Interface.  This is a variable (not a
+   #define) so it can be easily changed via some future ioctl() */
+static unsigned int cdu31a_port = 0;
+
+/*
+ * The following are I/O addresses of the various registers for the drive.  The
+ * comment for the base address also applies here.
+ */
+static volatile unsigned short sony_cd_cmd_reg;
+static volatile unsigned short sony_cd_param_reg;
+static volatile unsigned short sony_cd_write_reg;
+static volatile unsigned short sony_cd_control_reg;
+static volatile unsigned short sony_cd_status_reg;
+static volatile unsigned short sony_cd_result_reg;
+static volatile unsigned short sony_cd_read_reg;
+static volatile unsigned short sony_cd_fifost_reg;
+
+
+static int sony_spun_up = 0;               /* Has the drive been spun up? */
+
+static int sony_xa_mode = 0;               /* Is an XA disk in the drive
+					      and the drive a CDU31A? */
+
+static int sony_raw_data_mode = 1;         /* 1 if data tracks, 0 if audio.
+                                              For raw data reads. */
+
+static unsigned int sony_usage = 0;        /* How many processes have the
+                                              drive open. */
+
+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 int sony_toc_read = 0;		   /* Has the TOC been read for
+					      the drive? */
+
+static struct s_sony_subcode last_sony_subcode; /* Points to the last
+                                                   subcode address read */
+
+static volatile int sony_inuse = 0;  /* Is the drive in use?  Only one operation
+					at a time allowed */
+
+static struct wait_queue * sony_wait = NULL;	/* Things waiting for the drive */
+
+static struct task_struct *has_cd_task = NULL;  /* The task that is currently
+						   using the CDROM drive, or
+						   NULL if none. */
+
+static int is_double_speed = 0; /* Is the drive a CDU33A? */
+
+/*
+ * The audio status uses the values from read subchannel data as specified
+ * in include/linux/cdrom.h.
+ */
+static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;
+
+/*
+ * The following are a hack for pausing and resuming audio play.  The drive
+ * does not work as I would expect it, if you stop it then start it again,
+ * the drive seeks back to the beginning and starts over.  This holds the
+ * position during a pause so a resume can restart it.  It uses the
+ * audio status variable above to tell if it is paused.
+ */
+static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
+static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
+
+/* What IRQ is the drive using?  0 if none. */
+static int cdu31a_irq = 0;
+
+/* The interrupt handler will wake this queue up when it gets an
+   interrupts. */
+static struct wait_queue *cdu31a_irq_wait = NULL;
+
+static int curr_control_reg = 0; /* Current value of the control register */
+
+/* A disk changed variable.  When a disk change is detected, it will
+   all be set to TRUE.  As the upper layers ask for disk_changed status
+   it will be cleared. */
+static char disk_changed;
+
+/* Variable for using the readahead buffer.  The readahead buffer
+   is used for raw sector reads and for blocksizes that are smaller
+   than 2048 bytes. */
+static char readahead_buffer[CD_FRAMESIZE_RAW];
+static int readahead_dataleft = 0;
+static int readahead_bad = 0;
+
+/* Used to time a short period to abort an operation after the
+   drive has been idle for a while.  This keeps the light on
+   the drive from flashing for very long. */
+static struct timer_list cdu31a_abort_timer;
+
+/* Marks if the timeout has started an abort read.  This is used
+   on entry to the drive to tell the code to read out the status
+   from the abort read. */
+static int abort_read_started = 0;
+
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't.
+ */
+static int
+scd_disk_change(kdev_t full_dev)
+{
+   int retval;
+
+   retval = disk_changed;
+   disk_changed = 0;
+
+   return retval;
+}
+
+static inline void
+enable_interrupts(void)
+{
+   curr_control_reg |= (  SONY_ATTN_INT_EN_BIT
+                        | SONY_RES_RDY_INT_EN_BIT
+                        | SONY_DATA_RDY_INT_EN_BIT);
+   outb(curr_control_reg, sony_cd_control_reg);
+}
+
+static inline void
+disable_interrupts(void)
+{
+   curr_control_reg &= ~(  SONY_ATTN_INT_EN_BIT
+                         | SONY_RES_RDY_INT_EN_BIT
+                         | SONY_DATA_RDY_INT_EN_BIT);
+   outb(curr_control_reg, sony_cd_control_reg);
+}
+
+/*
+ * Wait a little while (used for polling the drive).  If in initialization,
+ * setting a timeout doesn't work, so just loop for a while.
+ */
+static inline void
+sony_sleep(void)
+{
+   unsigned long flags;
+
+   if (cdu31a_irq <= 0)
+   {
+      current->state = TASK_INTERRUPTIBLE;
+      current->timeout = jiffies;
+      schedule();
+   }
+   else /* Interrupt driven */
+   {
+      save_flags(flags);
+      cli();
+      enable_interrupts();
+      interruptible_sleep_on(&cdu31a_irq_wait);
+      restore_flags(flags);
+   }
+}
+
+
+/*
+ * The following are convenience routine to read various status and set
+ * various conditions in the drive.
+ */
+static inline int
+is_attention(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);
+}
+
+static inline int
+is_busy(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);
+}
+
+static inline int
+is_data_ready(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);
+}
+
+static inline int
+is_data_requested(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);
+}
+
+static inline int
+is_result_ready(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);
+}
+
+static inline int
+is_param_write_rdy(void)
+{
+   return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
+}
+
+static inline int
+is_result_reg_not_empty(void)
+{
+   return((inb(sony_cd_fifost_reg) & SONY_RES_REG_NOT_EMP_BIT) != 0);
+}
+
+static inline void
+reset_drive(void)
+{
+   curr_control_reg = 0;
+   outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_attention(void)
+{
+   outb(curr_control_reg | SONY_ATTN_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_result_ready(void)
+{
+   outb(curr_control_reg | SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_data_ready(void)
+{
+   outb(curr_control_reg | SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_param_reg(void)
+{
+   outb(curr_control_reg | SONY_PARAM_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline unsigned char
+read_status_register(void)
+{
+   return(inb(sony_cd_status_reg));
+}
+
+static inline unsigned char
+read_result_register(void)
+{
+   return(inb(sony_cd_result_reg));
+}
+
+static inline unsigned char
+read_data_register(void)
+{
+   return(inb(sony_cd_read_reg));
+}
+
+static inline void
+write_param(unsigned char param)
+{
+   outb(param, sony_cd_param_reg);
+}
+
+static inline void
+write_cmd(unsigned char cmd)
+{
+   outb(curr_control_reg | SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
+   outb(cmd, sony_cd_cmd_reg);
+}
+
+static void
+cdu31a_interrupt(int irq, struct pt_regs *regs)
+{
+   unsigned char val;
+
+   if (abort_read_started)
+   {
+      /* We might be waiting for an abort to finish.  Don't
+         disable interrupts yet, though, because we handle
+         this one here. */
+      /* Clear out the result registers. */
+      while (is_result_reg_not_empty())
+      {
+         val = read_result_register();
+      }
+      clear_data_ready();
+      clear_result_ready();
+
+      /* Clear out the data */
+      while (is_data_requested())
+      {
+         val = read_data_register();
+      }
+      abort_read_started = 0;
+
+      /* If something was waiting, wake it up now. */
+      if (cdu31a_irq_wait != NULL)
+      {
+         disable_interrupts();
+         wake_up(&cdu31a_irq_wait);
+      }
+   }
+   else if (cdu31a_irq_wait != NULL)
+   {
+      disable_interrupts();
+      wake_up(&cdu31a_irq_wait);
+   }
+   else
+   {
+      disable_interrupts();
+      printk("CDU31A: Got an interrupt but nothing was waiting\n");
+   }
+}
+
+/*
+ * Set the drive parameters so the drive will auto-spin-up when a
+ * disk is inserted.
+ */
+static void
+set_drive_params(void)
+{
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   unsigned char params[3];
+
+
+   params[0] = SONY_SD_AUTO_SPIN_DOWN_TIME;
+   params[1] = 0x00; /* Never spin down the drive. */
+   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                  params,
+                  2,
+                  res_reg,
+                  &res_size);
+   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+   {
+      printk("  Unable to set spin-down time: 0x%2.2x\n", res_reg[1]);
+   }
+
+   params[0] = SONY_SD_MECH_CONTROL;
+   params[1] = 0x03; /* Set auto spin up and auto eject */
+   if (is_double_speed)
+   {
+      params[1] |= 0x04; /* Set the drive to double speed if possible */
+   }
+   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                  params,
+                  2,
+                  res_reg,
+                  &res_size);
+   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+   {
+      printk("  Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
+   }
+}
+
+/*
+ * This code will reset the drive and attempt to restore sane parameters.
+ */
+static void
+restart_on_error(void)
+{
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   unsigned int retry_count;
+
+
+   printk("cdu31a: Resetting drive on error\n");
+   reset_drive();
+   retry_count = jiffies + SONY_RESET_TIMEOUT;
+   while ((retry_count > jiffies) && (!is_attention()))
+   {
+      sony_sleep();
+   }
+   set_drive_params();
+   do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+   {
+      printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]);
+   }
+
+   current->state = TASK_INTERRUPTIBLE;
+   current->timeout = jiffies + 2*HZ;
+   schedule();
+
+   do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
+   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+   {
+      printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]);
+   }
+}
+
+/*
+ * This routine writes data to the parameter register.  Since this should
+ * happen fairly fast, it is polled with no OS waits between.
+ */
+static int
+write_params(unsigned char *params,
+             int num_params)
+{
+   unsigned int retry_count;
+
+
+   retry_count = SONY_READY_RETRIES;
+   while ((retry_count > 0) && (!is_param_write_rdy()))
+   {
+      retry_count--;
+   }
+   if (!is_param_write_rdy())
+   {
+      return -EIO;
+   }
+
+   while (num_params > 0)
+   {
+      write_param(*params);
+      params++;
+      num_params--;
+   }
+
+   return 0;
+}
+
+
+/*
+ * The following reads data from the command result register.  It is a
+ * fairly complex routine, all status info flows back through this
+ * interface.  The algorithm is stolen directly from the flowcharts in
+ * the drive manual.
+ */
+static void
+get_result(unsigned char *result_buffer,
+           unsigned int *result_size)
+{
+   unsigned char a, b;
+   int i;
+   unsigned int retry_count;
+
+
+   while (handle_sony_cd_attention())
+      ;
+   /* Wait for the result data to be ready */
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
+   {
+      sony_sleep();
+
+      while (handle_sony_cd_attention())
+         ;
+   }
+   if (is_busy() || (!(is_result_ready())))
+   {
+#if DEBUG
+      printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+      result_buffer[0] = 0x20;
+      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+      *result_size = 2;
+      return;
+   }
+
+   /*
+    * Get the first two bytes.  This determines what else needs
+    * to be done.
+    */
+   clear_result_ready();
+   a = read_result_register();
+   *result_buffer = a;
+   result_buffer++;
+
+   /* Check for block error status result. */
+   if ((a & 0xf0) == 0x50)
+   {
+      *result_size = 1;
+      return;
+   }
+
+   b = read_result_register();
+   *result_buffer = b;
+   result_buffer++;
+   *result_size = 2;
+
+   /*
+    * 0x20 means an error occurred.  Byte 2 will have the error code.
+    * Otherwise, the command succeeded, byte 2 will have the count of
+    * how many more status bytes are coming.
+    *
+    * The result register can be read 10 bytes at a time, a wait for
+    * result ready to be asserted must be done between every 10 bytes.
+    */
+   if ((a & 0xf0) != 0x20)
+   {
+      if (b > 8)
+      {
+         for (i=0; i<8; i++)
+         {
+            *result_buffer = read_result_register();
+            result_buffer++;
+            (*result_size)++;
+         }
+         b = b - 8;
+
+         while (b > 10)
+         {
+            retry_count = SONY_READY_RETRIES;
+            while ((retry_count > 0) && (!is_result_ready()))
+            {
+               retry_count--;
+            }
+            if (!is_result_ready())
+            {
+#if DEBUG
+               printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+               result_buffer[0] = 0x20;
+               result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+               *result_size = 2;
+               return;
+            }
+
+            clear_result_ready();
+                                
+            for (i=0; i<10; i++)
+            {
+               *result_buffer = read_result_register();
+               result_buffer++;
+               (*result_size)++;
+            }
+            b = b - 10;
+         }
+
+         if (b > 0)
+         {
+            retry_count = SONY_READY_RETRIES;
+            while ((retry_count > 0) && (!is_result_ready()))
+            {
+               retry_count--;
+            }
+            if (!is_result_ready())
+            {
+#if DEBUG
+               printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+               result_buffer[0] = 0x20;
+               result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+               *result_size = 2;
+               return;
+            }
+         }
+      }
+
+      while (b > 0)
+      {
+         *result_buffer = read_result_register();
+         result_buffer++;
+         (*result_size)++;
+         b--;
+      }
+   }
+}
+
+/*
+ * Do a command that does not involve data transfer.  This routine must
+ * be re-entrant from the same task to support being called from the
+ * data operation code when an error occurs.
+ */
+static void
+do_sony_cd_cmd(unsigned char cmd,
+               unsigned char *params,
+               unsigned int num_params,
+               unsigned char *result_buffer,
+               unsigned int *result_size)
+{
+   unsigned int retry_count;
+   int num_retries;
+   int recursive_call;
+   unsigned long flags;
+
+
+   save_flags(flags);
+   cli();
+   if (current != has_cd_task) /* Allow recursive calls to this routine */
+   {
+      while (sony_inuse)
+      {
+         interruptible_sleep_on(&sony_wait);
+         if (current->signal & ~current->blocked)
+         {
+            result_buffer[0] = 0x20;
+            result_buffer[1] = SONY_SIGNAL_OP_ERR;
+            *result_size = 2;
+            restore_flags(flags);
+            return;
+         }
+      }
+      sony_inuse = 1;
+      has_cd_task = current;
+      recursive_call = 0;
+   }
+   else
+   {
+      recursive_call = 1;
+   }
+
+   num_retries = 0;
+retry_cd_operation:
+
+   while (handle_sony_cd_attention())
+      ;
+
+   sti();
+   
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ((retry_count > jiffies) && (is_busy()))
+   {
+      sony_sleep();
+      
+      while (handle_sony_cd_attention())
+         ;
+   }
+   if (is_busy())
+   {
+#if DEBUG
+      printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+      result_buffer[0] = 0x20;
+      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+      *result_size = 2;
+   }
+   else
+   {
+      clear_result_ready();
+      clear_param_reg();
+
+      write_params(params, num_params);
+      write_cmd(cmd);
+
+      get_result(result_buffer, result_size);
+   }
+
+   if (   ((result_buffer[0] & 0xf0) == 0x20)
+       && (num_retries < MAX_CDU31A_RETRIES))
+   {
+      num_retries++;
+      current->state = TASK_INTERRUPTIBLE;
+      current->timeout = jiffies + HZ/10; /* Wait .1 seconds on retries */
+      schedule();
+      goto retry_cd_operation;
+   }
+
+   if (!recursive_call)
+   {
+      has_cd_task = NULL;
+      sony_inuse = 0;
+      wake_up_interruptible(&sony_wait);
+   }
+
+   restore_flags(flags);
+}
+
+
+/*
+ * Handle an attention from the drive.  This will return 1 if it found one
+ * or 0 if not (if one is found, the caller might want to call again).
+ *
+ * This routine counts the number of consecutive times it is called
+ * (since this is always called from a while loop until it returns
+ * a 0), and returns a 0 if it happens too many times.  This will help
+ * prevent a lockup.
+ */
+static int
+handle_sony_cd_attention(void)
+{
+   unsigned char atten_code;
+   static int num_consecutive_attentions = 0;
+   volatile int val;
+
+
+   if (abort_read_started)
+   {
+      while (is_result_reg_not_empty())
+      {
+         val = read_result_register();
+      }
+      clear_data_ready();
+      clear_result_ready();
+      /* Clear out the data */
+      while (is_data_requested())
+      {
+         val = read_data_register();
+      }
+      abort_read_started = 0;
+      return(1);
+   }
+   else if (is_attention())
+   {
+      if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS)
+      {
+         printk("cdu31a: Too many consecutive attentions: %d\n",
+                num_consecutive_attentions);
+         num_consecutive_attentions = 0;
+         return(0);
+      }
+
+      clear_attention();
+      atten_code = read_result_register();
+
+      switch (atten_code)
+      {
+       /* Someone changed the CD.  Mark it as changed */
+      case SONY_MECH_LOADED_ATTN:
+         disk_changed = 1;
+	 sony_toc_read = 0;
+         sony_audio_status = CDROM_AUDIO_NO_STATUS;
+         sony_blocks_left = 0;
+         break;
+
+      case SONY_SPIN_DOWN_COMPLETE_ATTN:
+         /* Mark the disk as spun down. */
+         sony_spun_up = 0;
+         break;
+
+      case SONY_AUDIO_PLAY_DONE_ATTN:
+         sony_audio_status = CDROM_AUDIO_COMPLETED;
+         read_subcode();
+         break;
+
+      case SONY_EJECT_PUSHED_ATTN:
+         sony_audio_status = CDROM_AUDIO_INVALID;
+         break;
+
+      case SONY_LEAD_IN_ERR_ATTN:
+      case SONY_LEAD_OUT_ERR_ATTN:
+      case SONY_DATA_TRACK_ERR_ATTN:
+      case SONY_AUDIO_PLAYBACK_ERR_ATTN:
+         sony_audio_status = CDROM_AUDIO_ERROR;
+         break;
+      }
+
+      num_consecutive_attentions++;
+      return(1);
+   }
+
+   num_consecutive_attentions = 0;
+   return(0);
+}
+
+
+/* Convert from an integer 0-99 to BCD */
+static inline unsigned int
+int_to_bcd(unsigned int val)
+{
+   int retval;
+
+
+   retval = (val / 10) << 4;
+   retval = retval | val % 10;
+   return(retval);
+}
+
+
+/* Convert from BCD to an integer from 0-99 */
+static unsigned int
+bcd_to_int(unsigned int bcd)
+{
+   return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
+}
+
+
+/*
+ * Convert a logical sector value (like the OS would want to use for
+ * a block device) to an MSF format.
+ */
+static void
+log_to_msf(unsigned int log, unsigned char *msf)
+{
+   log = log + LOG_START_OFFSET;
+   msf[0] = int_to_bcd(log / 4500);
+   log = log % 4500;
+   msf[1] = int_to_bcd(log / 75);
+   msf[2] = int_to_bcd(log % 75);
+}
+
+
+/*
+ * Convert an MSF format to a logical sector.
+ */
+static unsigned int
+msf_to_log(unsigned char *msf)
+{
+   unsigned int log;
+
+
+   log = bcd_to_int(msf[2]);
+   log += bcd_to_int(msf[1]) * 75;
+   log += bcd_to_int(msf[0]) * 4500;
+   log = log - LOG_START_OFFSET;
+
+   return log;
+}
+
+
+/*
+ * Take in integer size value and put it into a buffer like
+ * the drive would want to see a number-of-sector value.
+ */
+static void
+size_to_buf(unsigned int size,
+            unsigned char *buf)
+{
+   buf[0] = size / 65536;
+   size = size % 65536;
+   buf[1] = size / 256;
+   buf[2] = size % 256;
+}
+
+/* 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
+   operation if the requested sector is not the next one from the
+   drive. */
+static int
+start_request(unsigned int sector,
+              unsigned int nsect,
+              int          read_nsect_only)
+{
+   unsigned char params[6];
+   unsigned int read_size;
+   unsigned int retry_count;
+
+
+   log_to_msf(sector, params);
+   /* If requested, read exactly what was asked. */
+   if (read_nsect_only)
+   {
+      read_size = nsect;
+   }
+   /*
+    * If the full read-ahead would go beyond the end of the media, trim
+    * it back to read just till the end of the media.
+    */
+   else if ((sector + nsect) >= sony_toc.lead_out_start_lba)
+   {
+      read_size = sony_toc.lead_out_start_lba - sector;
+   }
+   /* Read the full readahead amount. */
+   else
+   {
+      read_size = CDU31A_READAHEAD;
+   }
+   size_to_buf(read_size, &params[3]);
+
+   /*
+    * Clear any outstanding attentions and wait for the drive to
+    * complete any pending operations.
+    */
+   while (handle_sony_cd_attention())
+      ;
+
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ((retry_count > jiffies) && (is_busy()))
+   {
+      sony_sleep();
+      
+      while (handle_sony_cd_attention())
+         ;
+   }
+
+   if (is_busy())
+   {
+      printk("CDU31A: Timeout while waiting to issue command\n");
+      return(1);
+   }
+   else
+   {
+      /* Issue the command */
+      clear_result_ready();
+      clear_param_reg();
+
+      write_params(params, 6);
+      write_cmd(SONY_READ_BLKERR_STAT_CMD);
+
+      sony_blocks_left = read_size * 4;
+      sony_next_block = sector * 4;
+      readahead_dataleft = 0;
+      readahead_bad = 0;
+      return(0);
+   }
+}
+
+/* Abort a pending read operation.  Clear all the drive status and
+   readahead variables. */
+static void
+abort_read(void)
+{
+   unsigned char result_reg[2];
+   int           result_size;
+   volatile int  val;
+
+
+   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]);
+   }
+
+   while (is_result_reg_not_empty())
+   {
+      val = read_result_register();
+   }
+   clear_data_ready();
+   clear_result_ready();
+   /* Clear out the data */
+   while (is_data_requested())
+   {
+      val = read_data_register();
+   }
+
+   sony_blocks_left = 0;
+   readahead_dataleft = 0;
+   readahead_bad = 0;
+}
+
+/* Called when the timer times out.  This will abort the
+   pending read operation. */
+static void
+handle_abort_timeout(unsigned long data)
+{
+   /* If it is in use, ignore it. */
+   if (!sony_inuse)
+   {
+      /* We can't use abort_read(), because it will sleep
+         or schedule in the timer interrupt.  Just start
+         the operation, finish it on the next access to
+         the drive. */
+      clear_result_ready();
+      clear_param_reg();
+      write_cmd(SONY_ABORT_CMD);
+
+      sony_blocks_left = 0;
+      readahead_dataleft = 0;
+      readahead_bad = 0;
+      abort_read_started = 1;
+   }
+}
+
+/* Actually get data and status from the drive. */
+static void
+input_data(char         *buffer,
+           unsigned int bytesleft,
+           unsigned int nblocks,
+           unsigned int offset,
+           unsigned int skip)
+{
+   int i;
+   volatile unsigned char val;
+
+
+   /* If an XA disk on a CDU31A, skip the first 12 bytes of data from
+      the disk.  The real data is after that. */
+   if (sony_xa_mode)
+   {
+      for(i=0; i<CD_XA_HEAD; i++)
+      {
+         val = read_data_register();
+      }
+   }
+
+   clear_data_ready();
+
+   if (bytesleft == 2048) /* 2048 byte direct buffer transfer */
+   {
+      insb(sony_cd_read_reg, buffer, 2048);
+      readahead_dataleft = 0;
+   }
+   else
+   {
+      /* If the input read did not align with the beginning of the block,
+	 skip the necessary bytes. */
+      if (skip != 0)
+      {
+         insb(sony_cd_read_reg, readahead_buffer, skip);
+      }
+
+      /* Get the data into the buffer. */
+      insb(sony_cd_read_reg, &buffer[offset], bytesleft);
+
+      /* Get the rest of the data into the readahead buffer at the
+	 proper location. */
+      readahead_dataleft = (2048 - skip) - bytesleft;
+      insb(sony_cd_read_reg,
+           readahead_buffer + bytesleft,
+           readahead_dataleft);
+   }
+   sony_blocks_left -= nblocks;
+   sony_next_block += nblocks; 
+
+   /* If an XA disk, we have to clear out the rest of the unused
+      error correction data. */
+   if (sony_xa_mode)
+   {
+      for(i=0; i<CD_XA_TAIL; i++)
+      {
+         val = read_data_register();
+      }
+   }
+}
+
+/* read data from the drive.  Note the nsect must be <= 4. */
+static void
+read_data_block(char          *buffer,
+                unsigned int  block,
+                unsigned int  nblocks,
+                unsigned char res_reg[],
+                int           *res_size)
+{
+   unsigned int retry_count;
+   unsigned int bytesleft;
+   unsigned int offset;
+   unsigned int skip;
+
+
+   res_reg[0] = 0;
+   res_reg[1] = 0;
+   *res_size = 0;
+   bytesleft = nblocks * 512;
+   offset = 0;
+
+   /* If the data in the read-ahead does not match the block offset,
+      then fix things up. */
+   if (((block % 4) * 512) != ((2048 - readahead_dataleft) % 2048))
+   {
+      sony_next_block += block % 4;
+      sony_blocks_left -= block % 4;
+      skip = (block % 4) * 512;
+   }
+   else
+   {
+      skip = 0;
+   }
+
+   /* We have readahead data in the buffer, get that first before we
+      decide if a read is necessary. */
+   if (readahead_dataleft != 0)
+   {
+      if (bytesleft > readahead_dataleft)
+      {
+	 /* The readahead will not fill the requested buffer, but
+	    get the data out of the readahead into the buffer. */
+         memcpy(buffer,
+                readahead_buffer + (2048 - readahead_dataleft),
+                readahead_dataleft);
+         readahead_dataleft = 0;
+         bytesleft -= readahead_dataleft;
+         offset += readahead_dataleft;
+      }
+      else
+      {
+	 /* The readahead will fill the whole buffer, get the data
+	    and return. */
+         memcpy(buffer,
+                readahead_buffer + (2048 - readahead_dataleft),
+                bytesleft);
+         readahead_dataleft -= bytesleft;
+         bytesleft = 0;
+         sony_blocks_left -= nblocks;
+         sony_next_block += nblocks;
+
+	 /* If the data in the readahead is bad, return an error so the
+	    driver will abort the buffer. */
+         if (readahead_bad)
+         {
+            res_reg[0] = 0x20;
+            res_reg[1] = SONY_BAD_DATA_ERR;
+            *res_size = 2;
+         }
+
+         if (readahead_dataleft == 0)
+         {
+            readahead_bad = 0;
+         }
+
+         /* Final transfer is done for read command, get final result. */
+         if (sony_blocks_left == 0)
+         {
+            get_result(res_reg, res_size);
+         }
+         return;
+      }
+   }
+
+   /* Wait for the drive to tell us we have something */
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ((retry_count > jiffies) && !(is_data_ready()))
+   {
+      while (handle_sony_cd_attention())
+         ;
+
+      sony_sleep();
+   }
+   if (!(is_data_ready()))
+   {
+      if (is_result_ready())
+      {
+         get_result(res_reg, res_size);
+         if ((res_reg[0] & 0xf0) != 0x20)
+         {
+            printk("CDU31A: Got result that should have been error: %d\n",
+                   res_reg[0]);
+            res_reg[0] = 0x20;
+            res_reg[1] = SONY_BAD_DATA_ERR;
+            *res_size = 2;
+         }
+         abort_read();
+      }
+      else
+      {
+#if DEBUG
+         printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+         res_reg[0] = 0x20;
+         res_reg[1] = SONY_TIMEOUT_OP_ERR;
+         *res_size = 2;
+         abort_read();
+      }
+   }
+   else
+   {
+      input_data(buffer, bytesleft, nblocks, offset, skip);
+
+      /* Wait for the status from the drive. */
+      retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+      while ((retry_count > jiffies) && !(is_result_ready()))
+      {
+         while (handle_sony_cd_attention())
+            ;
+
+         sony_sleep();
+      }
+
+      if (!is_result_ready())
+      {
+#if DEBUG
+         printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+         res_reg[0] = 0x20;
+         res_reg[1] = SONY_TIMEOUT_OP_ERR;
+         *res_size = 2;
+         abort_read();
+      }
+      else
+      {
+         get_result(res_reg, res_size);
+
+	 /* If we got a buffer status, handle that. */
+         if ((res_reg[0] & 0xf0) == 0x50)
+         {
+
+            if (   (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
+                || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
+                || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT))
+            {
+	       /* The data was successful, but if data was read from
+		  the readahead  and it was bad, set the whole
+		  buffer as bad. */
+               if (readahead_bad)
+               {
+                  readahead_bad = 0;
+                  res_reg[0] = 0x20;
+                  res_reg[1] = SONY_BAD_DATA_ERR;
+                  *res_size = 2;
+               }
+            }
+            else
+            {
+               printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
+               res_reg[0] = 0x20;
+               res_reg[1] = SONY_BAD_DATA_ERR;
+               *res_size = 2;
+
+               /* Data is in the readahead buffer but an error was returned.
+                  Make sure future requests don't use the data. */
+               if (bytesleft != 2048)
+               {
+                  readahead_bad = 1;
+               }
+            }
+
+            /* Final transfer is done for read command, get final result. */
+            if (sony_blocks_left == 0)
+            {
+               get_result(res_reg, res_size);
+            }
+         }
+         else if ((res_reg[0] & 0xf0) != 0x20)
+         {
+            /* The drive gave me bad status, I don't know what to do.
+               Reset the driver and return an error. */
+            printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
+            restart_on_error();
+            res_reg[0] = 0x20;
+            res_reg[1] = SONY_BAD_DATA_ERR;
+            *res_size = 2;
+         }
+      }
+   }
+}
+
+/*
+ * 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
+ * bytes to help speed operations.  This especially helps since the OS
+ * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
+ * data access on a CD is done sequentially, this saves a lot of operations.
+ */
+static void
+do_cdu31a_request(void)
+{
+   int block;
+   int nblock;
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   int num_retries;
+   unsigned long flags;
+
+
+   /* 
+    * Make sure no one else is using the driver; wait for them
+    * to finish if it is so.
+    */
+   save_flags(flags);
+   cli();
+   while (sony_inuse)
+   {
+      interruptible_sleep_on(&sony_wait);
+      if (current->signal & ~current->blocked)
+      {
+         restore_flags(flags);
+         if (CURRENT && CURRENT->rq_status != RQ_INACTIVE)
+         {
+            end_request(0);
+         }
+         restore_flags(flags);
+         return;
+      }
+   }
+   sony_inuse = 1;
+   has_cd_task = current;
+
+   /* Get drive status before doing anything. */
+   while (handle_sony_cd_attention())
+      ;
+
+   sti();
+
+   /* If the timer is running, cancel it. */
+   if (cdu31a_abort_timer.next != NULL)
+   {
+      del_timer(&cdu31a_abort_timer);
+   }
+
+   while (1)
+   {
+cdu31a_request_startover:
+      /*
+       * The beginning here is stolen from the hard disk driver.  I hope
+       * it's right.
+       */
+      if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE)
+      {
+         goto end_do_cdu31a_request;
+      }
+
+      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);
+      }
+
+      /* I don't use INIT_REQUEST because it calls return, which would
+         return without unlocking the device.  It shouldn't matter,
+         but just to be safe... */
+      if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+      {
+	 panic(DEVICE_NAME ": request list destroyed");
+      }
+      if (CURRENT->bh)
+      {
+	 if (!CURRENT->bh->b_lock)
+         {
+	    panic(DEVICE_NAME ": block not locked");
+         }
+      }
+
+      block = CURRENT->sector;
+      nblock = CURRENT->nr_sectors;
+
+      if (!sony_toc_read)
+      {
+	 printk("CDU31A: TOC not read\n");
+	 end_request(0);
+	 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:
+         /*
+          * If the block address is invalid or the request goes beyond the end of
+          * the media, return an error.
+          */
+#if 0
+	 if ((block / 4) < sony_toc.start_track_lba)
+	 {
+            printk("CDU31A: Request before beginning of media\n");
+            end_request(0);
+            goto cdu31a_request_startover;
+	 }
+#endif
+         if ((block / 4) >= sony_toc.lead_out_start_lba)
+         {
+            printk("CDU31A: Request past end of media\n");
+            end_request(0);
+            goto cdu31a_request_startover;
+         }
+         if (((block + nblock) / 4) >= sony_toc.lead_out_start_lba)
+         {
+            printk("CDU31A: Request past end of media\n");
+            end_request(0);
+            goto cdu31a_request_startover;
+         }
+
+         num_retries = 0;
+
+try_read_again:
+         while (handle_sony_cd_attention())
+            ;
+
+         if (!sony_toc_read)
+         {
+	    printk("CDU31A: TOC not read\n");
+	    end_request(0);
+	    goto cdu31a_request_startover;
+         }
+
+	 /* If no data is left to be read from the drive, start the
+	    next request. */
+         if (sony_blocks_left == 0)
+         {
+            if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
+            {
+               end_request(0);
+               goto cdu31a_request_startover;
+            }
+         }
+	 /* If the requested block is not the next one waiting in
+	    the driver, abort the current operation and start a
+	    new one. */
+         else if (block != sony_next_block)
+         {
+#if DEBUG
+            printk("CDU31A Warning: Read for block %d, expected %d\n",
+                   block,
+                   sony_next_block);
+#endif
+            abort_read();
+            if (!sony_toc_read)
+            {
+	       printk("CDU31A: TOC not read\n");
+	       end_request(0);
+               goto cdu31a_request_startover;
+            }
+            if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
+            {
+               printk("CDU31a: start request failed\n");
+               end_request(0);
+               goto cdu31a_request_startover;
+            }
+         }
+
+         read_data_block(CURRENT->buffer, block, nblock, res_reg, &res_size);
+         if (res_reg[0] == 0x20)
+         {
+            if (num_retries > MAX_CDU31A_RETRIES)
+            {
+               end_request(0);
+               goto cdu31a_request_startover;
+            }
+
+            num_retries++;
+            if (res_reg[1] == SONY_NOT_SPIN_ERR)
+            {
+               do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+            }
+            else
+            {
+               printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]);
+            }
+            goto try_read_again;
+         }
+         else
+         {
+            end_request(1);
+         }
+         break;
+            
+      case WRITE:
+         end_request(0);
+         break;
+            
+      default:
+         panic("CDU31A: Unknown cmd");
+      }
+   }
+
+end_do_cdu31a_request:
+#if 0
+   /* After finished, cancel any pending operations. */
+   abort_read();
+#else
+   /* Start a timer to time out after a while to disable
+      the read. */
+   cdu31a_abort_timer.expires = jiffies + 2*HZ; /* Wait 2 seconds */
+   add_timer(&cdu31a_abort_timer);
+#endif
+
+   has_cd_task = NULL;
+   sony_inuse = 0;
+   wake_up_interruptible(&sony_wait);
+   restore_flags(flags);
+}
+
+/* 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
+ * successful.
+ */
+static void
+sony_get_toc(void)
+{
+   unsigned char res_reg[2];
+   unsigned int res_size;
+   unsigned char parms[1];
+   int session;
+
+
+#if DEBUG
+   printk("Entering sony_get_toc\n");
+#endif
+
+   if (!sony_toc_read)
+   {
+      /* The idea here is we keep asking for sessions until the command
+	 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;
+      while (1)
+      {
+#if DEBUG
+         printk("Trying session %d\n", session);
+#endif
+	 parms[0] = session;
+	 do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD,
+			parms,
+			1, 
+			res_reg,
+			&res_size);
+
+#if DEBUG
+         printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]);
+#endif
+
+	 if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+	 {
+	    /* An error reading the TOC, this must be past the last session. */
+	    break;
+         }
+
+         session++;
+
+         /* Let's not get carried away... */
+         if (session > 20)
+         {
+            return;
+         }
+      }
+
+      session--;
+
+#if DEBUG
+      printk("Reading session %d\n", session);
+#endif
+
+      parms[0] = session;
+      do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
+		     parms,
+		     1, 
+		     (unsigned char *) &sony_toc, 
+		     &res_size);
+      if ((res_size < 2) || ((sony_toc.exec_status[0] & 0xf0) == 0x20))
+      {
+	 /* An error reading the TOC.  Return without sony_toc_read
+	    set. */
+	 return;
+      }
+      
+      sony_toc_read = 1;
+
+      /* For points that do not exist, move the data over them
+	 to the right location. */
+      if (sony_toc.pointb0 != 0xb0)
+      {
+	 mcovlp(((char *) &sony_toc) + 27,
+		((char *) &sony_toc) + 18,
+		res_size - 18);
+	 res_size += 9;
+      }
+      if (sony_toc.pointb1 != 0xb1)
+      {
+	 mcovlp(((char *) &sony_toc) + 36,
+		((char *) &sony_toc) + 27,
+		res_size - 27);
+	 res_size += 9;
+      }
+      if (sony_toc.pointb2 != 0xb2)
+      {
+	 mcovlp(((char *) &sony_toc) + 45,
+		((char *) &sony_toc) + 36,
+		res_size - 36);
+	 res_size += 9;
+      }
+      if (sony_toc.pointb3 != 0xb3)
+      {
+	 mcovlp(((char *) &sony_toc) + 54,
+		((char *) &sony_toc) + 45,
+		res_size - 45);
+	 res_size += 9;
+      }
+      if (sony_toc.pointb4 != 0xb4)
+      {
+	 mcovlp(((char *) &sony_toc) + 63,
+		((char *) &sony_toc) + 54,
+		res_size - 54);
+	 res_size += 9;
+      }
+      if (sony_toc.pointc0 != 0xc0)
+      {
+	 mcovlp(((char *) &sony_toc) + 72,
+		((char *) &sony_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("Disk session %d, start track: %d, stop track: %d\n",
+	  session,
+	  sony_toc.start_track_lba,
+	  sony_toc.lead_out_start_lba);
+#endif
+   }
+#if DEBUG
+   printk("Leaving sony_get_toc\n");
+#endif
+}
+
+
+/*
+ * Search for a specific track in the table of contents.
+ */
+static int
+find_track(int track)
+{
+   int i;
+   int num_tracks;
+
+
+   num_tracks = sony_toc.last_track_num - sony_toc.first_track_num + 1;
+   for (i = 0; i < num_tracks; i++)
+   {
+      if (sony_toc.tracks[i].track == track)
+      {
+         return i;
+      }
+   }
+
+   return -1;
+}
+
+
+/*
+ * Read the subcode and put it int last_sony_subcode for future use.
+ */
+static int
+read_subcode(void)
+{
+   unsigned int res_size;
+
+
+   do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD,
+                  NULL,
+                  0, 
+                  (unsigned char *) &last_sony_subcode, 
+                  &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]);
+      return -EIO;
+   }
+
+   return 0;
+}
+
+
+/*
+ * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
+ * the drive is playing, the subchannel needs to be read (since it would be
+ * changing).  If the drive is paused or completed, the subcode information has
+ * already been stored, just use that.  The ioctl call wants things in decimal
+ * (not BCD), so all the conversions are done.
+ */
+static int
+sony_get_subchnl_info(long arg)
+{
+   struct cdrom_subchnl schi;
+
+
+   /* Get attention stuff */
+   while (handle_sony_cd_attention())
+      ;
+
+   sony_get_toc();
+   if (!sony_toc_read)
+   {
+      return -EIO;
+   }
+
+   verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
+   verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
+
+   memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
+   
+   switch (sony_audio_status)
+   {
+   case CDROM_AUDIO_PLAY:
+      if (read_subcode() < 0)
+      {
+         return -EIO;
+      }
+      break;
+
+   case CDROM_AUDIO_PAUSED:
+   case CDROM_AUDIO_COMPLETED:
+      break;
+
+   case CDROM_AUDIO_NO_STATUS:
+      schi.cdsc_audiostatus = sony_audio_status;
+      memcpy_tofs((char *) arg, &schi, sizeof(schi));
+      return 0;
+      break;
+
+   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]);
+   }
+   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);
+   }
+   
+   memcpy_tofs((char *) arg, &schi, sizeof(schi));
+   return 0;
+}
+
+/* Get audio data from the drive.  This is fairly complex because I
+   am looking for status and data at the same time, but if I get status
+   then I just look for data.  I need to get the status immediately so
+   the switch from audio to data tracks will happen quickly. */
+static void
+read_audio_data(char          *buffer,
+                unsigned char res_reg[],
+                int           *res_size)
+{
+   unsigned int retry_count;
+   int result_read;
+
+
+   res_reg[0] = 0;
+   res_reg[1] = 0;
+   *res_size = 0;
+   result_read = 0;
+
+   /* Wait for the drive to tell us we have something */
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+continue_read_audio_wait:
+   while (   (retry_count > jiffies)
+          && !(is_data_ready())
+          && !(is_result_ready() || result_read))
+   {
+      while (handle_sony_cd_attention())
+         ;
+
+      sony_sleep();
+   }
+   if (!(is_data_ready()))
+   {
+      if (is_result_ready() && !result_read)
+      {
+         get_result(res_reg, res_size);
+
+         /* Read block status and continue waiting for data. */
+         if ((res_reg[0] & 0xf0) == 0x50)
+         {
+            result_read = 1;
+            goto continue_read_audio_wait;
+         }
+         /* Invalid data from the drive.  Shut down the operation. */
+         else if ((res_reg[0] & 0xf0) != 0x20)
+         {
+            printk("CDU31A: Got result that should have been error: %d\n",
+                   res_reg[0]);
+            res_reg[0] = 0x20;
+            res_reg[1] = SONY_BAD_DATA_ERR;
+            *res_size = 2;
+         }
+         abort_read();
+      }
+      else
+      {
+#if DEBUG
+         printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+         res_reg[0] = 0x20;
+         res_reg[1] = SONY_TIMEOUT_OP_ERR;
+         *res_size = 2;
+         abort_read();
+      }
+   }
+   else
+   {
+      clear_data_ready();
+
+      /* 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);
+      }
+      else
+      {
+         /* Audio gets the whole 2352 bytes. */
+         insb(sony_cd_read_reg, buffer, CD_FRAMESIZE_RAW);
+      }
+
+      /* If I haven't already gotten the result, get it now. */
+      if (!result_read)
+      {
+         /* Wait for the drive to tell us we have something */
+         retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+         while ((retry_count > jiffies) && !(is_result_ready()))
+         {
+            while (handle_sony_cd_attention())
+               ;
+
+            sony_sleep();
+         }
+
+         if (!is_result_ready())
+         {
+#if DEBUG
+            printk("CDU31A timeout out %d\n", __LINE__);
+#endif
+            res_reg[0] = 0x20;
+            res_reg[1] = SONY_TIMEOUT_OP_ERR;
+            *res_size = 2;
+            abort_read();
+            return;
+         }
+         else
+         {
+            get_result(res_reg, res_size);
+         }
+      }
+
+      if ((res_reg[0] & 0xf0) == 0x50)
+      {
+         if (   (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
+             || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
+             || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT)
+             || (res_reg[0] == SONY_NO_ERR_DETECTION_STAT))
+         {
+            /* Ok, nothing to do. */
+         }
+         else
+         {
+            printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
+            res_reg[0] = 0x20;
+            res_reg[1] = SONY_BAD_DATA_ERR;
+            *res_size = 2;
+         }
+      }
+      else if ((res_reg[0] & 0xf0) != 0x20)
+      {
+         /* The drive gave me bad status, I don't know what to do.
+            Reset the driver and return an error. */
+         printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
+         restart_on_error();
+         res_reg[0] = 0x20;
+         res_reg[1] = SONY_BAD_DATA_ERR;
+         *res_size = 2;
+      }
+   }
+}
+
+/* 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)
+{
+   int retval;
+   unsigned char params[2];
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   unsigned int cframe;
+   unsigned long flags;
+
+   /* 
+    * Make sure no one else is using the driver; wait for them
+    * to finish if it is so.
+    */
+   save_flags(flags);
+   cli();
+   while (sony_inuse)
+   {
+      interruptible_sleep_on(&sony_wait);
+      if (current->signal & ~current->blocked)
+      {
+         restore_flags(flags);
+         return -EAGAIN;
+      }
+   }
+   sony_inuse = 1;
+   has_cd_task = current;
+   restore_flags(flags);
+
+   if (!sony_spun_up)
+   {
+      scd_open (inode, NULL);
+   }
+
+   /* Set the drive to do raw operations. */
+   params[0] = SONY_SD_DECODE_PARAM;
+   params[1] = 0x06 | sony_raw_data_mode;
+   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                  params,
+                  2,
+                  res_reg,
+                  &res_size);
+   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+   {
+      printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
+      return -EIO;
+   }
+
+   /* From here down, we have to goto exit_read_audio instead of returning
+      because the drive parameters have to be set back to data before
+      return. */
+
+   retval = 0;
+   /* start_request clears out any readahead data, so it should be safe. */
+   if (start_request(ra->addr.lba, ra->nframes, 1))
+   {
+      retval = -EIO;
+      goto exit_read_audio;
+   }
+
+   /* For every requested frame. */
+   cframe = 0;
+   while (cframe < ra->nframes)
+   {
+      read_audio_data(readahead_buffer, res_reg, &res_size);
+      if ((res_reg[0] & 0xf0) == 0x20)
+      {
+         if (res_reg[1] == SONY_BAD_DATA_ERR)
+         {
+            printk("CDU31A: Data error on audio sector %d\n",
+                   ra->addr.lba + cframe);
+         }
+         else if (res_reg[1] == SONY_ILL_TRACK_R_ERR)
+         {
+            /* Illegal track type, change track types and start over. */
+            sony_raw_data_mode = (sony_raw_data_mode) ? 0 : 1;
+
+            /* Set the drive mode. */
+            params[0] = SONY_SD_DECODE_PARAM;
+            params[1] = 0x06 | sony_raw_data_mode;
+            do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                           params,
+                           2,
+                           res_reg,
+                           &res_size);
+            if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+            {
+               printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
+               retval = -EIO;
+               goto exit_read_audio;
+            }
+
+            /* Restart the request on the current frame. */
+            if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1))
+            {
+               retval = -EIO;
+               goto exit_read_audio;
+            }
+
+            /* Don't go back to the top because don't want to get into
+               and infinite loop.  A lot of code gets duplicated, but
+               that's no big deal, I don't guess. */
+            read_audio_data(readahead_buffer, res_reg, &res_size);
+            if ((res_reg[0] & 0xf0) == 0x20)
+            {
+               if (res_reg[1] == SONY_BAD_DATA_ERR)
+               {
+                  printk("CDU31A: Data error on audio sector %d\n",
+                         ra->addr.lba + cframe);
+               }
+               else
+               {
+                  printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
+                         ra->addr.lba + cframe,
+                         res_reg[1]);
+                  retval = -EIO;
+                  goto exit_read_audio;
+               }
+            }
+            else
+            {
+               memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
+                           (char *) readahead_buffer,
+                           CD_FRAMESIZE_RAW);
+            }
+         }
+         else
+         {
+            printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
+                   ra->addr.lba + cframe,
+                   res_reg[1]);
+            retval = -EIO;
+            goto exit_read_audio;
+         }
+      }
+      else
+      {
+         memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
+                     (char *) readahead_buffer,
+                     CD_FRAMESIZE_RAW);
+      }
+
+      cframe++;
+   }
+
+   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]);
+      retval = -EIO;
+      goto exit_read_audio;
+   }
+
+exit_read_audio:
+
+   /* Set the drive mode back to the proper one for the disk. */
+   params[0] = SONY_SD_DECODE_PARAM;
+   if (!sony_xa_mode)
+   {
+      params[1] = 0x0f;
+   }
+   else
+   {
+      params[1] = 0x07;
+   }
+   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                  params,
+                  2,
+                  res_reg,
+                  &res_size);
+   if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+   {
+      printk("CDU31A: Unable to reset decode params: 0x%2.2x\n", res_reg[1]);
+      return -EIO;
+   }
+
+   has_cd_task = NULL;
+   sony_inuse = 0;
+   wake_up_interruptible(&sony_wait);
+
+   return(retval);
+}
+
+static int
+do_sony_cd_cmd_chk(const char *name,
+                   unsigned char cmd,
+                   unsigned char *params,
+                   unsigned int num_params,
+		   unsigned char *result_buffer,
+                   unsigned int *result_size)
+{      
+      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);
+         return -EIO;
+      }
+      return 0;
+}
+ 
+/*
+ * The big ugly ioctl handler.
+ */
+static int scd_ioctl(struct inode *inode,
+          struct file  *file,
+          unsigned int  cmd,
+          unsigned long arg)
+{
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   unsigned char params[7];
+   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 */
+      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+
+      /*
+       * Spin the drive down, ignoring the error if the disk was
+       * already not spinning.
+       */
+      sony_audio_status = CDROM_AUDIO_NO_STATUS;
+      return do_sony_cd_cmd_chk("STOP",SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+   case CDROMPAUSE:     /* Pause the drive */
+      if(do_sony_cd_cmd_chk("PAUSE", SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size))
+      	return -EIO;
+      /* Get the current position and save it for resuming */
+      if (read_subcode() < 0)
+      {
+         return -EIO;
+      }
+      cur_pos_msf[0] = last_sony_subcode.abs_msf[0];
+      cur_pos_msf[1] = last_sony_subcode.abs_msf[1];
+      cur_pos_msf[2] = last_sony_subcode.abs_msf[2];
+      sony_audio_status = CDROM_AUDIO_PAUSED;
+      return 0;
+      break;
+
+   case CDROMRESUME:    /* Start the drive after being paused */
+      if (sony_audio_status != CDROM_AUDIO_PAUSED)
+      {
+         return -EINVAL;
+      }
+      
+      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[0] = 0x03;
+      if(do_sony_cd_cmd_chk("RESUME",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0)
+      	return -EIO;
+      sony_audio_status = CDROM_AUDIO_PLAY;
+      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);
+      memcpy_fromfs(&(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[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];
+      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)
+         {
+            return -EIO;
+         }
+         
+         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);
+         memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
+      }
+      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;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         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;
+         
+         memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry));
+         
+         /* Lead out is handled separately since it is special. */
+         if (loc_entry.cdte_track == CDROM_LEADOUT)
+         {
+            loc_entry.cdte_adr = sony_toc.address2;
+            loc_entry.cdte_ctrl = sony_toc.control2;
+            msf_val = sony_toc.lead_out_start_msf;
+         }
+         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;
+         }
+         
+         /* Logical buffer address or MSF format requested? */
+         if (loc_entry.cdte_format == CDROM_LBA)
+         {
+            loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+         }
+         else if (loc_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));
+         }
+         memcpy_tofs(entry, &loc_entry, sizeof(*entry));
+      }
+      return 0;
+      break;
+
+   case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+      {
+         struct cdrom_ti ti;
+         int track_idx;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
+         if(i<0)
+         	return i;
+         
+         memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
+         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));
+         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];
+         
+         /*
+          * 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))
+         {
+            log_to_msf(msf_to_log(sony_toc.lead_out_start_msf)-1,
+                       &(params[4]));
+         }
+         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]));
+         }
+         params[0] = 0x03;
+         
+         do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+         
+         do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
+         
+         if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+         {
+            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]);
+            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];
+         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;
+         
+         memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+         params[0] = SONY_SD_AUDIO_VOLUME;
+         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);
+
+      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
+				 raw data tracks. */
+      {
+         struct cdrom_read_audio ra;
+
+
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ra));
+         if(i<0)
+         	return i;
+         memcpy_fromfs(&ra, (char *) arg, sizeof(ra));
+
+         i=verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes);
+         if(i<0)
+         	return i;
+
+         if (ra.addr_format == CDROM_LBA)
+         {
+            if (   (ra.addr.lba >= sony_toc.lead_out_start_lba)
+                || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba))
+            {
+               return -EINVAL;
+            }
+         }
+         else if (ra.addr_format == CDROM_MSF)
+         {
+            if (   (ra.addr.msf.minute >= 75)
+                || (ra.addr.msf.second >= 60)
+                || (ra.addr.msf.frame >= 75))
+            {
+               return -EINVAL;
+            }
+
+            ra.addr.lba = (  (ra.addr.msf.minute * 4500)
+                           + (ra.addr.msf.second * 75)
+                           + ra.addr.msf.frame);
+            if (   (ra.addr.lba >= sony_toc.lead_out_start_lba)
+                || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba))
+            {
+               return -EINVAL;
+            }
+
+            /* I know, this can go negative on an unsigned.  However,
+               the first thing done to the data is to add this value,
+               so this should compensate and allow direct msf access. */
+            ra.addr.lba -= LOG_START_OFFSET;
+         }
+         else
+         {
+            return -EINVAL;
+         }
+
+         return(read_audio(&ra, inode));
+      }
+      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)
+{
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   int num_spin_ups;
+   unsigned char params[2];
+
+
+   if ((filp) && filp->f_mode & 2)
+      return -EROFS;
+
+   if (!sony_spun_up)
+   {
+      num_spin_ups = 0;
+
+respinup_on_open:
+      do_sony_cd_cmd(SONY_SPIN_UP_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)))
+      {
+         printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
+         return -EIO;
+      }
+      
+      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 is already playing, it's ok.  */
+         if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
+         {
+            goto drive_spinning;
+         }
+
+         /* 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 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);
+         
+         return -EIO;
+      }
+
+      sony_get_toc();
+      if (!sony_toc_read)
+      {
+         do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+         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)
+          && (!is_double_speed))
+      {
+         params[0] = SONY_SD_DECODE_PARAM;
+         params[1] = 0x07;
+         do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                        params,
+                        2,
+                        res_reg,
+                        &res_size);
+         if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+         {
+            printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]);
+         }
+         sony_xa_mode = 1;
+      }
+      /* A non-XA disk.  Set the parms back if necessary. */
+      else if (sony_xa_mode)
+      {
+         params[0] = SONY_SD_DECODE_PARAM;
+         params[1] = 0x0f;
+         do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                        params,
+                        2,
+                        res_reg,
+                        &res_size);
+         if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+         {
+            printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]);
+         }
+         sony_xa_mode = 0;
+      }
+
+      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;
+
+   return 0;
+}
+
+
+/*
+ * 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 void
+scd_release(struct inode *inode,
+         struct file *filp)
+{
+   unsigned char res_reg[12];
+   unsigned int  res_size;
+
+
+   if (sony_usage > 0)
+   {
+      sony_usage--;
+      MOD_DEC_USE_COUNT;
+   }
+   if (sony_usage == 0)
+   {
+      sync_dev(inode->i_rdev);
+      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+      sony_spun_up = 0;
+   }
+}
+
+
+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,                   /* select */
+   scd_ioctl,              /* ioctl */
+   NULL,                   /* mmap */
+   scd_open,               /* open */
+   scd_release,            /* release */
+   NULL,                   /* fsync */
+   NULL,                   /* fasync */
+   scd_disk_change,        /* media_change */
+   NULL                    /* revalidate */
+};
+
+
+/* The different types of disc loading mechanisms supported */
+static const char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
+
+static void
+get_drive_configuration(unsigned short base_io,
+                        unsigned char res_reg[],
+                        unsigned int *res_size)
+{
+   int retry_count;
+
+
+   /* Set the base address */
+   cdu31a_port = base_io;
+
+   /* Set up all the register locations */
+   sony_cd_cmd_reg = cdu31a_port + SONY_CMD_REG_OFFSET;
+   sony_cd_param_reg = cdu31a_port + SONY_PARAM_REG_OFFSET;
+   sony_cd_write_reg = cdu31a_port + SONY_WRITE_REG_OFFSET;
+   sony_cd_control_reg = cdu31a_port + SONY_CONTROL_REG_OFFSET;
+   sony_cd_status_reg = cdu31a_port + SONY_STATUS_REG_OFFSET;
+   sony_cd_result_reg = cdu31a_port + SONY_RESULT_REG_OFFSET;
+   sony_cd_read_reg = cdu31a_port + SONY_READ_REG_OFFSET;
+   sony_cd_fifost_reg = cdu31a_port + SONY_FIFOST_REG_OFFSET;
+
+   /*
+    * Check to see if anything exists at the status register location.
+    * I don't know if this is a good way to check, but it seems to work
+    * ok for me.
+    */
+   if (read_status_register() != 0xff)
+   {
+      /*
+       * Reset the drive and wait for attention from it (to say it's reset).
+       * If you don't wait, the next operation will probably fail.
+       */
+      reset_drive();
+      retry_count = jiffies + SONY_RESET_TIMEOUT;
+      while ((retry_count > jiffies) && (!is_attention()))
+      {
+         sony_sleep();
+      }
+
+#if 0
+      /* If attention is never seen probably not a CDU31a present */
+      if (!is_attention())
+      {
+         res_reg[0] = 0x20;
+         return;
+      }
+#endif
+
+      /*
+       * Get the drive configuration.
+       */
+      do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD,
+                     NULL,
+                     0,
+                     (unsigned char *) res_reg,
+                     res_size);
+      return;
+   }
+
+   /* Return an error */
+   res_reg[0] = 0x20;
+}
+
+#ifndef MODULE
+/*
+ * Set up base I/O and interrupts, called from main.c.
+ */
+void
+cdu31a_setup(char *strings,
+	     int  *ints)
+{
+   if (ints[0] > 0)
+   {
+      cdu31a_port = ints[1];
+   }
+   if (ints[0] > 1)
+   {
+      cdu31a_irq = ints[2];
+   }
+   if ((strings != NULL) && (*strings != '\0'))
+   {
+      if (strcmp(strings, "PAS") == 0)
+      {
+	 sony_pas_init = 1;
+      }
+      else
+      {
+	 printk("CDU31A: Unknown interface type: %s\n", strings);
+      }
+   }
+}
+#endif
+
+static int cdu31a_block_size;
+
+/*
+ * Initialize the driver.
+ */
+#ifdef MODULE
+#define cdu31a_init init_module
+#endif
+
+int
+cdu31a_init(void)
+{
+   struct s_sony_drive_config drive_config;
+   unsigned int res_size;
+   int i;
+   int drive_found;
+   int tmp_irq;
+
+
+   /*
+    * According to Alex Freed (freed@europa.orion.adobe.com), this is
+    * required for the Fusion CD-16 package.  If the sound driver is
+    * loaded, it should work fine, but just in case...
+    *
+    * The following turn on the CD-ROM interface for a Fusion CD-16.
+    */
+   if (sony_pas_init)
+   {
+      outb(0xbc, 0x9a01);
+      outb(0xe2, 0x9a01);
+   }
+
+   drive_found = 0;
+
+   /* Setting the base I/O address to 0xffff will disable it. */
+   if (cdu31a_port == 0xffff)
+   {
+   }
+   else if (cdu31a_port != 0)
+   {
+      tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */
+      cdu31a_irq = 0;
+
+      get_drive_configuration(cdu31a_port,
+			      drive_config.exec_status,
+			      &res_size);
+      if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
+      {
+	 drive_found = 1;
+      }
+
+      cdu31a_irq = tmp_irq;
+   }
+   else
+   {
+      cdu31a_irq = 0;
+      i = 0;
+      while (   (cdu31a_addresses[i].base != 0)
+	     && (!drive_found))
+      {
+	 if (check_region(cdu31a_addresses[i].base, 4)) {
+	    i++;
+	    continue;
+	 }
+	 get_drive_configuration(cdu31a_addresses[i].base,
+				 drive_config.exec_status,
+				 &res_size);
+	 if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
+	 {
+	    drive_found = 1;
+	    cdu31a_irq = cdu31a_addresses[i].int_num;
+	 }
+	 else
+	 {
+	    i++;
+	 }
+      }
+   }
+
+   if (drive_found)
+   {
+      request_region(cdu31a_port, 4,"cdu31a");
+      
+      if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
+      {
+	 printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
+	 return -EIO;
+      }
+
+      if (SONY_HWC_DOUBLE_SPEED(drive_config))
+      {
+	 is_double_speed = 1;
+      }
+
+      tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */
+      cdu31a_irq = 0;
+
+      set_drive_params();
+
+      cdu31a_irq = tmp_irq;
+      
+      if (cdu31a_irq > 0)
+      {
+	 if (request_irq(cdu31a_irq, cdu31a_interrupt, SA_INTERRUPT, "cdu31a"))
+	 {
+	    printk("Unable to grab IRQ%d for the CDU31A driver\n", cdu31a_irq);
+	    cdu31a_irq = 0;
+	 }
+      }
+      
+      printk("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("  Capabilities: %s",
+	     load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
+      if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
+      {
+	 printk(", audio");
+      }
+      if (SONY_HWC_EJECT(drive_config))
+      {
+	 printk(", eject");
+      }
+      if (SONY_HWC_LED_SUPPORT(drive_config))
+      {
+	 printk(", LED");
+      }
+      if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
+      {
+	 printk(", elec. Vol");
+      }
+      if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
+      {
+	 printk(", sep. Vol");
+      }
+      if (is_double_speed)
+      {
+	 printk(", double speed");
+      }
+      if (cdu31a_irq > 0)
+      {
+	 printk(", irq %d", cdu31a_irq);
+      }
+      printk("\n");
+
+      blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+      read_ahead[MAJOR_NR] = CDU31A_READAHEAD;
+      cdu31a_block_size = 1024; /* 1kB default block size */
+      /* use 'mount -o block=2048' */
+      blksize_size[MAJOR_NR] = &cdu31a_block_size;
+      
+      cdu31a_abort_timer.next = NULL;
+      cdu31a_abort_timer.prev = NULL;
+      cdu31a_abort_timer.function = handle_abort_timeout;
+   }
+
+
+   disk_changed = 1;
+   
+   if (drive_found)
+   {
+      return(0);
+   }
+   else
+   {
+      return -EIO;
+   }
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+   if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL))    
+   {
+      printk("Can't unregister cdu31a\n");
+      return;
+   }
+   release_region(cdu31a_port,4);
+   printk("cdu31a module released.\n");
+}   
+#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v1.3.35/linux/drivers/cdrom/cm206.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/cm206.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,1249 @@
+/* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card.
+   Copyright (c) 1995 David van Leeuwen.
+   
+     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:
+ Started 25 jan 1994. Waiting for documentation...
+ 22 feb 1995: 0.1a first reasonably safe polling driver.
+	      Two major bugs, one in read_sector and one in 
+	      do_cm206_request, happened to cancel!
+ 25 feb 1995: 0.2a first reasonable interrupt driven version of above.
+              uart writes are still done in polling mode. 
+ 25 feb 1995: 0.21a writes also in interrupt mode, still some
+	      small bugs to be found... Larger buffer. 
+  2 mrt 1995: 0.22 Bug found (cd-> nowhere, interrupt was called in
+              initialization), read_ahead of 16. Timeouts implemented.
+	      unclear if they do something...
+  7 mrt 1995: 0.23 Start of background read-ahead.
+ 18 mrt 1995: 0.24 Working background read-ahead. (still problems)
+ 26 mrt 1995: 0.25 Multi-session ioctl added (kernel v1.2).
+              Statistics implemented, though separate stats206.h.
+	      Accessible trough ioctl 0x1000 (just a number).
+	      Hard to choose between v1.2 development and 1.1.75.
+	      Bottom-half doesn't work with 1.2...
+	      0.25a: fixed... typo. Still problems...
+  1 apr 1995: 0.26 Module support added. Most bugs found. Use kernel 1.2.n.
+  5 apr 1995: 0.27 Auto-probe for the adapter card base address.
+              Auto-probe for the adaptor card irq line.
+  7 apr 1995: 0.28 Added lilo setup support for base address and irq.
+              Use major number 32 (not in this source), officially
+	      assigned to this driver.
+  9 apr 1995: 0.29 Added very limited audio support. Toc_header, stop, pause,
+              resume, eject. Play_track ignores track info, because we can't 
+	      read a table-of-contents entry. Toc_entry is implemented
+	      as a `placebo' function: always returns start of disc. 
+  3 may 1995: 0.30 Audio support completed. The get_toc_entry function
+              is implemented as a binary search. 
+ 15 may 1995: 0.31 More work on audio stuff. Workman is not easy to 
+              satisfy; changed binary search into linear search.
+	      Auto-probe for base address somewhat relaxed.
+  1 jun 1995: 0.32 Removed probe_irq_on/off for module version.
+ 10 jun 1995: 0.33 Workman still behaves funny, but you should be
+              able to eject and substitute another disc.
+
+ An adaption of 0.33 is included in linux-1.3.7 by Eberhard Moenkeberg
+
+ 18 jul 1996: 0.34 Patch by Heiko Eissfeldt included, mainly considering 
+              verify_area's in the ioctls. Some bugs introduced by 
+	      EM considering the base port and irq fixed. 
+ * 
+ * Parts of the code are based upon lmscd.c written by Kai Petzke,
+ * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin
+ * Harriss, but any off-the-shelf dynamic programming algorithm won't
+ * be able to find them.
+ *
+ * The cm206 drive interface and the cm260 adapter card seem to be 
+ * sufficiently different from their cm205/cm250 counterparts
+ * in order to write a complete new driver.
+ * 
+ * I call all routines connected to the Linux kernel something
+ * with `cm206' in it, as this stuff is too series-dependent. 
+ * 
+ * Currently, my limited knowledge is based on:
+ * - The Linux Kernel Hacker's guide, v. 0.5 , by Michael J. Johnson
+ * - Linux Kernel Programmierung, by Michael Beck and others
+ * - Philips/LMS cm206 and cm226 product specification
+ * - Philips/LMS cm260 product specification
+ *
+ *                       David van Leeuwen, david@tm.tno.nl.  */
+#define VERSION "0.34"
+
+#ifdef MODULE			/* OK, so some of this is stolen */
+#include <linux/module.h>	
+#include <linux/version.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[]=UTS_RELEASE;
+#endif
+#else 
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif MODULE
+
+#include <linux/errno.h>	/* These include what we really need */
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+
+#define MAJOR_NR CM206_CDROM_MAJOR
+#include <linux/blk.h>
+#include <linux/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 
+   parameter `auto'.
+*/
+static int auto_probe=1;	/* Yes, why not? */
+
+static int cm206_base = CM206_BASE;
+static int cm206_irq = CM206_IRQ; 
+
+#undef DEBUG
+#undef DEBUG_SECTORS
+#define STATISTICS
+#undef AUTO_PROBE_MODULE
+
+#define POLLOOP 10000
+#define READ_AHEAD 1		/* defines private buffer, waste! */
+#define BACK_AHEAD 1		/* defines adapter-read ahead */
+#define DATA_TIMEOUT (3*HZ)	/* measured in jiffies (10 ms) */
+#define UART_TIMEOUT (5*HZ/100)
+#define DSB_TIMEOUT (7*HZ)	/* time for the slowest command to finish */
+
+#define RAW_SECTOR_SIZE 2352	/* ok, is also defined in cdrom.h */
+#define ISO_SECTOR_SIZE 2048
+
+#ifdef STATISTICS		/* keep track of errors in counters */
+#include <linux/stats206.h>
+#define stats(i) ++cd->stats[st_ ## i]; \
+                 cd->last_stat[st_ ## i] = cd->stat_counter++;
+#else
+#define stats(i) (void) 0
+#endif
+
+#ifdef DEBUG			/* from lmscd.c */
+#define debug(a) printk a
+#else
+#define debug(a) (void) 0
+#endif
+
+typedef unsigned char uch;	/* 8-bits */
+typedef unsigned short ush;	/* 16-bits */
+
+struct toc_struct{
+  uch track, fsm[3], q0;
+};
+
+struct cm206_struct {
+  ush intr_ds;	 /* data status read on last interrupt */
+  ush intr_ls;	 /* uart line status read on last interrupt*/
+  uch intr_ur;			/* uart receive buffer */
+  uch dsb, cc;	 /* drive status byte and condition (error) code */
+  uch fool;
+  int command;			/* command to be written to te uart */
+  int openfiles;
+  ush sector[READ_AHEAD*RAW_SECTOR_SIZE/2]; /* buffered cd-sector */
+  int sector_first, sector_last;	/* range of these sector */
+  struct wait_queue * uart;	/* wait for interrupt */
+  struct wait_queue * data;
+  struct timer_list timer;	/* time-out */
+  char timed_out;
+  signed char max_sectors;
+  char wait_back;		/* we're waiting for a background-read */
+  char background;		/* is a read going on in the background? */
+  int adapter_first;		/* if so, that's the starting sector */
+  int adapter_last;
+  char fifo_overflowed;
+  uch disc_status[7];		/* result of get_disc_status command */
+#ifdef STATISTICS
+  int stats[NR_STATS];
+  int last_stat[NR_STATS];	/* `time' at which stat was stat */
+  int stat_counter;
+#endif  
+  struct toc_struct toc[101];	/* The whole table of contents + lead-out */
+  uch q[10];			/* Last read q-channel info */
+  uch audio_status[5];		/* last read position on pause */
+};
+
+#define DISC_STATUS cd->disc_status[0]
+#define FIRST_TRACK cd->disc_status[1]
+#define LAST_TRACK cd->disc_status[2]
+#define PAUSED cd->audio_status[0] /* misuse this memory byte! */
+#define PLAY_TO cd->toc[0]	/* toc[0] records end-time in play */
+
+static struct cm206_struct * cd;
+
+/* First, we define some polling functions. These are actually
+   only being used in the initialization. */
+
+void send_command_polled(int command)
+{
+  int loop=POLLOOP;
+  while (!(inw(r_line_status) & ls_transmitter_buffer_empty) && loop>0) 
+    --loop;
+  outw(command, r_uart_transmit);
+}
+
+uch receive_echo_polled(void)
+{
+  int loop=POLLOOP;
+  while (!(inw(r_line_status) & ls_receive_buffer_full) && loop>0) --loop;
+  return ((uch) inw(r_uart_receive));
+}
+
+uch send_receive_polled(int command)
+{
+  send_command_polled(command);
+  return receive_echo_polled();
+}
+
+/* The interrupt handler. When the cm260 generates an interrupt, very
+   much care has to be taken in reading out the registers in the right
+   order; in case of a receive_buffer_full interrupt, first the
+   uart_receive must be read, and then the line status again to
+   de-assert the interrupt line. It took me a couple of hours to find
+   this out:-( 
+
+   The function reset_cm206 appears to cause an interrupt, because
+   pulling up the INIT line clears both the uart-write-buffer /and/
+   the uart-write-buffer-empty mask. We call this a `lost interrupt,'
+   as there seems so reason for this to happen.
+*/
+
+static void cm206_interrupt(int sig, struct pt_regs * regs) /* you rang? */
+{
+  volatile ush fool;
+    cd->intr_ds = inw(r_data_status); /* resets data_ready, data_error,
+					 crc_error, sync_error, toc_ready 
+					 interrupts */
+    cd->intr_ls = inw(r_line_status); /* resets overrun bit */
+    /* receive buffer full? */
+    if (cd->intr_ls & ls_receive_buffer_full) {	
+      cd->intr_ur = inb(r_uart_receive); /* get order right! */
+      cd->intr_ls = inw(r_line_status); /* resets rbf interrupt */
+      if (!cd->background && cd->uart) wake_up_interruptible(&cd->uart);
+    }
+    /* data ready in fifo? */
+    else if (cd->intr_ds & ds_data_ready) { 
+      if (cd->background) ++cd->adapter_last;
+      if ((cd->wait_back || !cd->background) && cd->data) 
+	  wake_up_interruptible(&cd->data);
+      stats(data_ready);
+    }
+    /* ready to issue a write command? */
+    else if (cd->command && cd->intr_ls & ls_transmitter_buffer_empty) {
+      outw(dc_normal | (inw(r_data_status) & 0x7f), r_data_control);
+      outw(cd->command, r_uart_transmit);
+      cd->command=0;
+      if (!cd->background) wake_up_interruptible(&cd->uart);
+    }
+    /* now treat errors (at least, identify them for debugging) */
+    else if (cd->intr_ds & ds_fifo_overflow) {
+      debug(("Fifo overflow at sectors 0x%x\n", cd->sector_first));
+      fool = inw(r_fifo_output_buffer);	/* de-assert the interrupt */
+      cd->fifo_overflowed=1;	/* signal one word less should be read */
+      stats(fifo_overflow);
+    }
+    else if (cd->intr_ds & ds_data_error) {
+      debug(("Data error at sector 0x%x\n", cd->sector_first));
+      stats(data_error);
+    }
+    else if (cd->intr_ds & ds_crc_error) {
+      debug(("CRC error at sector 0x%x\n", cd->sector_first));
+      stats(crc_error);
+    }
+    else if (cd->intr_ds & ds_sync_error) {
+      debug(("Sync at sector 0x%x\n", cd->sector_first));
+      stats(sync_error);
+    }
+    else if (cd->intr_ds & ds_toc_ready) {
+				/* do something appropiate */
+    }
+    /* couldn't see why this interrupt, maybe due to init */
+    else {			
+      outw(dc_normal | READ_AHEAD, r_data_control);
+      stats(lost_intr);
+    }
+  if (cd->background && (cd->adapter_last-cd->adapter_first == cd->max_sectors
+      || cd->fifo_overflowed))
+    mark_bh(CM206_BH);	/* issue a stop read command */
+  stats(interrupt);
+}
+
+/* we have put the address of the wait queue in who */
+void cm206_timeout(unsigned long who)
+{
+  cd->timed_out = 1;
+  wake_up_interruptible((struct wait_queue **) who);
+}
+
+/* This function returns 1 if a timeout occurred, 0 if an interrupt
+   happened */
+int sleep_or_timeout(struct wait_queue ** wait, int timeout)
+{
+  cd->timer.data=(unsigned long) wait;
+  cd->timer.expires = jiffies + timeout;
+  add_timer(&cd->timer);
+  interruptible_sleep_on(wait);
+  del_timer(&cd->timer);
+  if (cd->timed_out) {
+    cd->timed_out = 0;
+    return 1;
+  }
+  else return 0;
+}
+
+void cm206_delay(int jiffies) 
+{
+  struct wait_queue * wait = NULL;
+  sleep_or_timeout(&wait, jiffies);
+}
+
+void send_command(int command)
+{
+  if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) {
+    cd->command = command;
+    cli();			/* don't interrupt before sleep */
+    outw(dc_mask_sync_error | dc_no_stop_on_error | 
+	 (inw(r_data_status) & 0x7f), r_data_control);
+    /* interrupt routine sends command */
+    if (sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
+      debug(("Time out on write-buffer\n"));
+      stats(write_timeout);
+      outw(command, r_uart_transmit);
+    }
+  }
+  else outw(command, r_uart_transmit);
+}
+
+uch receive_echo(void)
+{
+  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
+      sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
+    debug(("Time out on receive-buffer\n"));
+    stats(receive_timeout);
+    return ((uch) inw(r_uart_receive));
+  }
+  return cd->intr_ur;
+}
+
+inline uch send_receive(int command)
+{
+  send_command(command);
+  return receive_echo();
+}
+
+uch wait_dsb(void)
+{
+  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
+      sleep_or_timeout(&cd->uart, DSB_TIMEOUT)) {
+    debug(("Time out on Drive Status Byte\n"));
+    stats(dsb_timeout);
+    return ((uch) inw(r_uart_receive));
+  }
+  return cd->intr_ur;
+}
+
+int type_0_command(int command, int expect_dsb)
+{
+  int e;
+  if (command != (e=send_receive(command))) {
+    debug(("command 0x%x echoed as 0x%x\n", command, e));
+    stats(echo);
+    return -1;
+  }
+  if (expect_dsb) {
+    cd->dsb = wait_dsb();	/* wait for command to finish */
+  }
+  return 0;
+}
+
+int type_1_command(int command, int bytes, uch * status) /* returns info */
+{
+  int i;
+  if (type_0_command(command,0)) return -1;
+  for(i=0; i<bytes; i++) 
+    status[i] = send_receive(c_gimme);
+  return 0;
+}  
+
+/* This function resets the adapter card. We'd better not do this too */
+/* often, because it tends to generate `lost interrupts.' */
+void reset_cm260(void)
+{
+  outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control);
+  udelay(10);			/* 3.3 mu sec minimum */
+  outw(dc_normal | READ_AHEAD, r_data_control);
+}
+
+/* fsm: frame-sec-min from linear address */
+void fsm(int lba, uch * fsm) 
+{
+  fsm[0] = lba % 75;
+  lba /= 75; lba += 2;
+  fsm[1] = lba % 60; fsm[2] = lba / 60;
+}
+
+inline int fsm2lba(uch * fsm) 
+{
+  return fsm[0] + 75*(fsm[1]-2 + 60*fsm[2]);
+}
+
+inline int f_s_m2lba(uch f, uch s, uch m)
+{
+  return f + 75*(s-2 + 60*m);
+}
+
+int start_read(int start) 
+{
+  uch read_sector[4] = {c_read_data, };
+  int i, e;
+
+  fsm(start, &read_sector[1]);
+  for (i=0; i<4; i++) 
+    if (read_sector[i] != (e=send_receive(read_sector[i]))) {
+      debug(("read_sector: %x echoes %x\n", read_sector[i], e));
+      stats(echo);
+      return -1;
+    }
+  return 0;
+}
+
+int stop_read(void)
+{
+  type_0_command(c_stop,0);
+  if(receive_echo() != 0xff) {
+    debug(("c_stop didn't send 0xff\n"));
+    stats(stop_0xff);
+    return -1;
+  }
+  return 0;
+}  
+
+/* This function starts to read sectors in adapter memory, the
+   interrupt routine should stop the read. In fact, the bottom_half
+   routine takes care of this. Set a flag `background' in the cd
+   struct to indicate the process. */
+
+int read_background(int start, int reading)
+{
+  if (cd->background) return -1; /* can't do twice */
+  outw(dc_normal | BACK_AHEAD, r_data_control);
+  if (!reading && start_read(start)) return -2;
+  cd->adapter_first = cd->adapter_last = start; 
+  cd->background = 1;		/* flag a read is going on */
+  return 0;
+}
+
+int read_sector(int start)
+{
+  if (cd->background) {
+    cd->background=0;
+    cd->adapter_last = -1;	/* invalidate adapter memory */
+    stop_read();
+  }
+  cd->fifo_overflowed=0;
+  reset_cm260();		/* empty fifo etc. */
+  if (start_read(start)) return -1;
+  if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
+    debug(("Read timed out sector 0x%x\n", start));
+    stats(read_timeout);
+    stop_read();
+    return -3;		
+  }
+  insw(r_fifo_output_buffer, cd->sector, READ_AHEAD*RAW_SECTOR_SIZE/2);
+  if (read_background(start+READ_AHEAD,1)) stats(read_background);
+  cd->sector_first = start; cd->sector_last = start+READ_AHEAD;
+  stats(read_restarted);
+  return 0;
+}
+
+/* The function of bottom-half is to send a stop command to the drive
+   This isn't easy because the routine is not `owned' by any process;
+   we can't go to sleep! The variable cd->background gives the status:
+   0 no read pending
+   1 a read is pending
+   2 c_stop waits for write_buffer_empty
+   3 c_stop waits for receive_buffer_full: echo
+   4 c_stop waits for receive_buffer_full: 0xff
+*/
+
+void cm206_bh(void * unused)
+{
+  debug(("bh: %d\n", cd->background));
+  switch (cd->background) {
+  case 1:
+    stats(bh);
+    if (!(cd->intr_ls & ls_transmitter_buffer_empty)) {
+      cd->command = c_stop;
+      outw(dc_mask_sync_error | dc_no_stop_on_error | 
+	   (inw(r_data_status) & 0x7f), r_data_control);
+      cd->background=2;
+      break;			/* we'd better not time-out here! */
+    }
+    else outw(c_stop, r_uart_transmit);
+    /* fall into case 2: */
+  case 2:			
+    /* the write has been satisfied by interrupt routine */
+    cd->background=3;
+    break;
+  case 3:
+    if (cd->intr_ur != c_stop) {
+      debug(("cm206_bh: c_stop echoed 0x%x\n", cd->intr_ur));
+      stats(echo);
+    }
+    cd->background++;
+    break;
+  case 4:
+    if (cd->intr_ur != 0xff) {
+      debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->intr_ur));
+      stats(stop_0xff);
+    }
+    cd->background=0;
+  }
+}
+
+void get_drive_status(void)
+{
+  uch status[2];
+  type_1_command(c_drive_status, 2, status); /* this might be done faster */
+  cd->dsb=status[0];
+  cd->cc=status[1];
+}
+
+void get_disc_status(void)
+{
+  if (type_1_command(c_disc_status, 7, cd->disc_status)) {
+    debug(("get_disc_status: error\n"));
+  }
+}
+
+static int cm206_open(struct inode *ip, struct file *fp)
+{
+  if (!cd->openfiles) {
+    cd->background=0;
+    reset_cm260();
+    cd->adapter_last = -1;	/* invalidate adapter memory */
+    cd->sector_last = -1;
+    get_drive_status();
+    if (cd->dsb & dsb_tray_not_closed) {
+      int i=0;
+      type_0_command(c_close_tray, 1);
+      while (i++<10 && cd->dsb & dsb_drive_not_ready) {
+	cm206_delay(100);
+	get_drive_status();
+      }
+    }
+    if (cd->dsb & (dsb_not_useful)) return -EIO;
+    if (!(cd->dsb & dsb_disc_present)) return -ENODATA;
+    if (cd->dsb & dsb_possible_media_change) {
+      memset(cd->toc, 0, sizeof(cd->toc));
+      memset(cd->audio_status, 0, sizeof(cd->audio_status));
+    }
+    get_disc_status();
+    type_0_command(c_lock_tray,1);
+    if (!(cd->dsb & dsb_tray_locked)) {
+      debug(("Couldn't lock tray\n"));
+    }
+#if 0
+    if (!(DISC_STATUS & cds_all_audio))
+      read_background(16,0);	/* do something useful */
+#endif
+  }
+  ++cd->openfiles; MOD_INC_USE_COUNT;
+  stats(open);
+  return 0;
+}
+
+static void cm206_release(struct inode *ip, struct file *fp)
+{
+  if (cd->openfiles==1) {
+    if (cd->background) {
+      cd->background=0;
+      stop_read();
+    }
+    type_0_command(c_unlock_tray,1);
+    cd->sector_last = -1;	/* Make our internal buffer invalid */
+    FIRST_TRACK = 0;	/* No valid disc status */
+    sync_dev(ip -> i_rdev);	/* These two lines are stolen */
+    invalidate_buffers(ip -> i_rdev);
+  }
+  --cd->openfiles; MOD_DEC_USE_COUNT;
+}
+
+/* Empty buffer empties $sectors$ sectors of the adapter card buffer,
+ * and then reads a sector in kernel memory.  */
+void empty_buffer(int sectors) 
+{
+  while (sectors>=0) {
+    insw(r_fifo_output_buffer, cd->sector + cd->fifo_overflowed, 
+	 RAW_SECTOR_SIZE/2 - cd->fifo_overflowed);
+    --sectors;
+    ++cd->adapter_first;	/* update the current adapter sector */
+    cd->fifo_overflowed=0;	/* reset overflow bit */
+    stats(sector_transferred);
+  } 
+  cd->sector_first=cd->adapter_first-1;
+  cd->sector_last=cd->adapter_first; /* update the buffer sector */
+}
+
+/* try_adapter. This function determines of the requested sector is is
+   in adapter memory, or will appear there soon. Returns 0 upon
+   success */
+int try_adapter(int sector)
+{
+  if (cd->adapter_first <= sector && sector < cd->adapter_last) { 
+    /* sector is in adapter memory */
+    empty_buffer(sector - cd->adapter_first);
+    return 0;
+  }
+  else if (cd->background==1 && cd->adapter_first <= sector
+	   && sector < cd->adapter_first+cd->max_sectors) {
+    /* a read is going on, we can wait for it */
+    cd->wait_back=1;
+    while (sector >= cd->adapter_last) {
+      if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
+	debug(("Timed out during background wait: %d %d %d %d\n", sector, 
+	       cd->adapter_last, cd->adapter_first, cd->background));
+	stats(back_read_timeout);
+	cd->wait_back=0;
+	return -1;
+      }
+    }
+    cd->wait_back=0;
+    empty_buffer(sector - cd->adapter_first);
+    return 0;
+  }
+  else return -2;
+}
+
+/* This is not a very smart implementation. We could optimize for 
+   consecutive block numbers. I'm not convinced this would really
+   bring down the processor load. */
+static void do_cm206_request(void)
+{
+  long int i, cd_sec_no;
+  int quarter, error; 
+  uch * source, * dest;
+  
+  while(1) {	 /* repeat until all requests have been satisfied */
+    INIT_REQUEST;
+    if (CURRENT == NULL || CURRENT->rq_status == RQ_INACTIVE)
+      return;
+    if (CURRENT->cmd != READ) {
+      debug(("Non-read command %d on cdrom\n", CURRENT->cmd));
+      end_request(0);
+      continue;
+    }
+    error=0;
+    for (i=0; i<CURRENT->nr_sectors; i++) {
+      cd_sec_no = (CURRENT->sector+i)/4; /* 4 times 512 bytes */
+      quarter = (CURRENT->sector+i) % 4; 
+      dest = CURRENT->buffer + i*512;
+      /* is already in buffer memory? */
+      if (cd->sector_first <= cd_sec_no && cd_sec_no < cd->sector_last) {
+	source = ((uch *) cd->sector) + 16 + 
+	  quarter*512 + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE;
+ 	memcpy(dest, source, 512); 
+      }
+      else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) {
+	source =  ((uch *) cd->sector)+16+quarter*512;
+	memcpy(dest, source, 512); 
+      }
+      else {
+	error=1;
+      }
+    }
+    end_request(!error);
+  }
+}
+
+int get_multi_session_info(struct cdrom_multisession * mssp)
+{
+  if (!FIRST_TRACK) get_disc_status();
+  if (mssp) {
+    if (DISC_STATUS & cds_multi_session) { /* multi-session */
+      if (mssp->addr_format == CDROM_LBA)
+      	mssp->addr.lba = fsm2lba(&cd->disc_status[3]);
+      else {
+      	mssp->addr.msf.frame = cd->disc_status[3];
+      	mssp->addr.msf.second = cd->disc_status[4];
+      	mssp->addr.msf.minute = cd->disc_status[5];
+      }
+      mssp->xa_flag = 1;
+    } else {
+      mssp->xa_flag = 0;
+    }
+    return 1;
+  }
+  return 0;
+}
+
+/* Audio support. I've tried very hard, but the cm206 drive doesn't 
+   seem to have a get_toc (table-of-contents) function, while i'm
+   pretty sure it must read the toc upon disc insertion. Therefore
+   this function has been implemented through a binary search 
+   strategy. All track starts that happen to be found are stored in
+   cd->toc[], for future use. 
+
+   I've spent a whole day on a bug that only shows under Workman---
+   I don't get it. Tried everything, nothing works. If workman asks
+   for track# 0xaa, it'll get the wrong time back. Any other program
+   receives the correct value. I'm stymied.
+*/
+
+/* seek seeks to address lba. It does wait to arrive there. */
+void seek(int lba)
+{
+  int i;
+  uch seek_command[4]={c_seek, };
+  
+  fsm(lba, &seek_command[1]);
+  for (i=0; i<4; i++) type_0_command(seek_command[i], 0);
+  cd->dsb = wait_dsb();
+}
+
+uch bcdbin(unsigned char bcd)	/* stolen from mcd.c! */
+{
+  return (bcd >> 4)*10 + (bcd & 0xf);
+} 
+
+inline uch normalize_track(uch track) 
+{
+  if (track<1) return 1;
+  if (track>LAST_TRACK) return LAST_TRACK+1;
+  return track;
+}
+
+/* This function does a binary search for track start. It records all
+ * tracks seen in the process. Input $track$ must be between 1 and
+ * #-of-tracks+1 */
+int get_toc_lba(uch track)
+{
+  int max=74*60*75-150, min=0;
+  int i, lba, l, old_lba=0;
+  uch * q = cd->q;
+  uch ct;			/* current track */
+  int binary=0;
+  const skip = 3*60*75;
+
+  for (i=track; i>0; i--) if (cd->toc[i].track) {
+    min = fsm2lba(cd->toc[i].fsm);
+    break;
+  }
+  lba = min + skip;		/* 3 minutes */
+  do {
+    seek(lba); 
+    type_1_command(c_read_current_q, 10, q);
+    ct = normalize_track(q[1]);
+    if (!cd->toc[ct].track) {
+      l = q[9]-bcdbin(q[5]) + 75*(q[8]-bcdbin(q[4])-2 + 
+				  60*(q[7]-bcdbin(q[3])));
+      cd->toc[ct].track=q[1];	/* lead out still 0xaa */
+      fsm(l, cd->toc[ct].fsm);
+      cd->toc[ct].q0 = q[0];	/* contains adr and ctrl info */
+/*
+      if (ct==LAST_TRACK+1) 
+	printk("Leadout %x %x %x %x %d %d %d \n", q[1], q[3], q[4], q[5],
+	       q[7], q[8], q[9]);
+*/
+      if (ct==track) return l;
+    }
+    old_lba=lba;
+    if (binary) {
+      if (ct < track) min = lba; else max = lba;
+      lba = (min+max)/2; 
+    } else {
+      if(ct < track) lba += skip;
+      else {
+	binary=1;
+	max = lba; min = lba - skip;
+	lba = (min+max)/2;
+      }
+    }
+  } while (lba!=old_lba);
+  return lba;
+}
+
+void update_toc_entry(uch track) 
+{
+  track = normalize_track(track);
+  if (!cd->toc[track].track) get_toc_lba(track);
+}
+
+int read_toc_header(struct cdrom_tochdr * hp)
+{
+  if (!FIRST_TRACK) get_disc_status();
+  if (hp && DISC_STATUS & cds_all_audio) { /* all audio */
+    int i;
+    hp->cdth_trk0 = FIRST_TRACK;
+    hp->cdth_trk1 = LAST_TRACK;
+    cd->toc[1].track=1;		/* fill in first track position */
+    for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i];
+    update_toc_entry(LAST_TRACK+1);		/* find most entries */
+    return 1;
+  }
+  return 0;
+}  
+
+void play_from_to_msf(struct cdrom_msf* msfp)
+{
+  uch play_command[] = {c_play, 
+	   msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0,
+	   msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2, 2};
+  int i;
+  for (i=0; i<9; i++) type_0_command(play_command[i], 0);
+  for (i=0; i<3; i++) 
+    PLAY_TO.fsm[i] = play_command[i+4];
+  PLAY_TO.track = 0;		/* say no track end */
+  cd->dsb = wait_dsb();
+}  
+
+void play_from_to_track(int from, int to)
+{
+  uch play_command[8] = {c_play, };
+  int i;
+
+  if (from==0) {		/* continue paused play */
+    for (i=0; i<3; i++) { 
+      play_command[i+1] = cd->audio_status[i+2];
+      play_command[i+4] = PLAY_TO.fsm[i];
+    }
+  } else {
+    update_toc_entry(from); update_toc_entry(to+1);
+    for (i=0; i<3; i++) {
+      play_command[i+1] = cd->toc[from].fsm[i];
+      PLAY_TO.fsm[i] = play_command[i+4] = cd->toc[to+1].fsm[i];
+    }
+    PLAY_TO.track = to; 
+  }
+  for (i=0; i<7; i++) type_0_command(play_command[i],0);
+  for (i=0; i<2; i++) type_0_command(0x2, 0); /* volume */
+  cd->dsb = wait_dsb();
+}
+
+int get_current_q(struct cdrom_subchnl * qp)
+{
+  int i;
+  uch * q = cd->q;
+  if (type_1_command(c_read_current_q, 10, q)) return 0;
+/*  q[0] = bcdbin(q[0]); Don't think so! */
+  for (i=2; i<6; i++) q[i]=bcdbin(q[i]); 
+  qp->cdsc_adr = q[0] & 0xf; qp->cdsc_ctrl = q[0] >> 4;	/* from mcd.c */
+  qp->cdsc_trk = q[1];  qp->cdsc_ind = q[2];
+  if (qp->cdsc_format == CDROM_MSF) {
+    qp->cdsc_reladdr.msf.minute = q[3];
+    qp->cdsc_reladdr.msf.second = q[4];
+    qp->cdsc_reladdr.msf.frame = q[5];
+    qp->cdsc_absaddr.msf.minute = q[7];
+    qp->cdsc_absaddr.msf.second = q[8];
+    qp->cdsc_absaddr.msf.frame = q[9];
+  } else {
+    qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]);
+    qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]);
+  }
+  get_drive_status();
+  if (cd->dsb & dsb_play_in_progress) 
+    qp->cdsc_audiostatus = CDROM_AUDIO_PLAY ;
+  else if (PAUSED) 
+    qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED;
+  else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
+  return 1;
+}
+
+void get_toc_entry(struct cdrom_tocentry * ep)
+{
+  uch track = normalize_track(ep->cdte_track);
+  update_toc_entry(track);
+  if (ep->cdte_format == CDROM_MSF) {
+    ep->cdte_addr.msf.frame = cd->toc[track].fsm[0];
+    ep->cdte_addr.msf.second = cd->toc[track].fsm[1];
+    ep->cdte_addr.msf.minute = cd->toc[track].fsm[2];
+  } 
+  else ep->cdte_addr.lba = fsm2lba(cd->toc[track].fsm);
+  ep->cdte_adr = cd->toc[track].q0 & 0xf; 
+  ep->cdte_ctrl = cd->toc[track].q0 >> 4;
+  ep->cdte_datamode=0;
+}
+  
+/* Ioctl. I have made the statistics accessible through an ioctl
+   call. The constant is defined in cm206.h, it shouldn't clash with
+   the standard Linux ioctls. Multisession info is gathered at
+   run-time, this may turn out to be slow. */
+
+static int cm206_ioctl(struct inode * inode, struct file * file, 
+		       unsigned int cmd, unsigned long arg)
+{
+  switch (cmd) {
+#ifdef STATISTICS
+  case CM206CTL_GET_STAT:
+    if (arg >= NR_STATS) return -EINVAL;
+    else return cd->stats[arg];
+  case CM206CTL_GET_LAST_STAT:
+    if (arg >= NR_STATS) return -EINVAL;
+    else return cd->last_stat[arg];
+#endif    
+  case CDROMMULTISESSION: {
+    struct cdrom_multisession ms_info;
+    int st;
+    stats(ioctl_multisession);
+
+    st=verify_area(VERIFY_WRITE, (void *) arg, 
+		   sizeof(struct cdrom_multisession));
+    if (st) return (st);
+    memcpy_fromfs(&ms_info, (struct cdrom_multisession *) arg,
+		  sizeof(struct cdrom_multisession));
+    get_multi_session_info(&ms_info);
+    memcpy_tofs((struct cdrom_multisession *) arg, &ms_info, 
+		  sizeof(struct cdrom_multisession));
+    return 0;
+  }
+  case CDROMRESET:		/* If needed, it's probably too late anyway */
+    stop_read();
+    reset_cm260();
+    outw(dc_normal | dc_break | READ_AHEAD, r_data_control);
+    udelay(1000);		/* 750 musec minimum */
+    outw(dc_normal | READ_AHEAD, r_data_control);
+    cd->sector_last = -1;	/* flag no data buffered */
+    cd->adapter_last = -1;    
+    return 0;
+  }
+
+  get_drive_status();
+  if (cd->dsb & (dsb_drive_not_ready | dsb_tray_not_closed) )
+    return -EAGAIN; 
+
+  switch (cmd) {
+  case CDROMREADTOCHDR: {
+    struct cdrom_tochdr header;
+    int st;
+
+    st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(header));
+    if (st) return (st);
+    if (read_toc_header(&header)) {
+      memcpy_tofs((struct cdrom_tochdr *) arg, &header, sizeof(header));
+      return 0;
+    }
+    else return -ENODATA;
+  }
+  case CDROMREADTOCENTRY: {	
+    struct cdrom_tocentry entry;
+    int st;
+
+    st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry));
+    if (st) return (st);
+    memcpy_fromfs(&entry, (struct cdrom_tocentry *) arg, sizeof entry);
+    get_toc_entry(&entry);
+    memcpy_tofs((struct cdrom_tocentry *) arg, &entry, sizeof entry);
+    return 0;
+  }
+  case CDROMPLAYMSF: {
+    struct cdrom_msf msf;
+    int st;
+
+    st=verify_area(VERIFY_READ, (void *) arg, sizeof(msf));
+    if (st) return (st);
+    memcpy_fromfs(&msf, (struct cdrom_mdf *) arg, sizeof msf);
+    play_from_to_msf(&msf);
+    return 0;
+  }
+  case CDROMPLAYTRKIND: {
+    struct cdrom_ti track_index;
+    int st;
+
+    st=verify_area(VERIFY_READ, (void *) arg, sizeof(track_index));
+    if (st) return (st);
+    memcpy_fromfs(&track_index, (struct cdrom_ti *) arg, sizeof(track_index));
+    play_from_to_track(track_index.cdti_trk0, track_index.cdti_trk1);
+    return 0;
+  }
+  case CDROMSTOP: 
+    PAUSED=0;
+    if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1);
+    return 0;
+  case CDROMPAUSE: 
+    if (cd->dsb & dsb_play_in_progress) {
+      type_0_command(c_stop, 1);
+      type_1_command(c_audio_status, 5, cd->audio_status);
+      PAUSED=1;	/* say we're paused */
+    }
+    return 0;
+  case CDROMRESUME:
+    if (PAUSED) play_from_to_track(0,0);
+    PAUSED=0;
+    return 0;
+  case CDROMEJECT:
+    PAUSED=0;
+    if (cd->openfiles == 1) {	/* Must do an open before an eject! */
+      type_0_command(c_open_tray,1);
+      memset(cd->toc, 0, sizeof(cd->toc));
+      memset(cd->disc_status, 0, sizeof(cd->disc_status));
+      return 0;
+    }
+    else return -EBUSY;
+  case CDROMSTART:
+  case CDROMVOLCTRL:
+    return 0;
+  case CDROMSUBCHNL: {
+    struct cdrom_subchnl q;
+    int st;
+
+    st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(q));
+    if (st) return (st);
+    memcpy_fromfs(&q, (struct cdrom_subchnl *) arg, sizeof q);
+    if (get_current_q(&q)) {
+      memcpy_tofs((struct cdrom_subchnl *) arg, &q, sizeof q);
+      return 0;
+    }
+    else return -cmd;
+  }
+  case CDROM_GET_UPC: {
+    uch upc[10];
+    int st;
+
+    st=verify_area(VERIFY_WRITE, (void *) arg, 8);
+    if (st) return (st);
+    if (type_1_command(c_read_upc, 10, upc)) return -EIO;
+    memcpy_tofs((uch *) arg, &upc[1], 8);
+    return 0;
+  } 
+  default:
+    debug(("Unknown ioctl call 0x%x\n", cmd));
+    return -EINVAL;
+  }
+}     
+
+/* from lmscd.c */
+static struct file_operations cm206_fops = {
+	NULL,			/* lseek */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir */
+	NULL,			/* select */
+	cm206_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	cm206_open,		/* open */
+	cm206_release,		/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* media_change */
+	NULL			/* revalidate */
+};
+
+/* This routine gets called during init if thing go wrong, can be used
+ * in cleanup_module as well. */
+void cleanup(int level)
+{
+  switch (level) {
+  case 4: 
+    if (unregister_blkdev(MAJOR_NR, "cm206")) {
+      printk("Can't unregister cm206\n");
+      return;
+    }
+  case 3: 
+    free_irq(cm206_irq);
+  case 2: 
+  case 1: 
+    kfree(cd);
+    release_region(cm206_base, 16);
+  default:
+  }
+}
+
+/* This function probes for the adapter card. It returns the base
+   address if it has found the adapter card. One can specify a base 
+   port to probe specifically, or 0 which means span all possible
+   bases. 
+
+   Linus says it is too dangerous to use writes for probing, so we
+   stick with pure reads for a while. Hope that 8 possible ranges,
+   check_region, 15 bits of one port and 6 of another make things
+   likely enough to accept the region on the first hit...
+ */
+int probe_base_port(int base)
+{
+  int b=0x300, e=0x370;		/* this is the range of start addresses */
+  volatile int fool;
+#if 0
+  const pattern1=0x65, pattern2=0x1a;
+#endif
+
+  if (base) b=e=base;
+  for (base=b; base<=e; base += 0x10) {
+    if (check_region(base, 0x10)) continue;
+    fool = inw(base+2);		/* empty possibly uart_receive_buffer */
+    if((inw(base+6) & 0xffef) != 0x0001 || /* line_status */
+       (inw(base) & 0xad00) != 0) /* data status */
+      continue;
+#if 0				/* writes... dangerous... */
+    outw(dc_normal | pattern1, base+8); 
+    if ((inw(base) & 0x7f) != pattern1) continue;
+    outw(dc_normal | pattern2, base+8);
+    if ((inw(base) & 0x7f) != pattern2) continue;
+    outw(dc_normal | READ_AHEAD, base+8);
+#endif
+    return(base);
+  }
+  return 0;
+}
+
+#if !defined(MODULE) || defined(AUTO_PROBE_MODULE)
+/* Probe for irq# nr. If nr==0, probe for all possible irq's. */
+int probe_irq(int nr) {
+  int irqs, irq;
+  outw(dc_normal | READ_AHEAD, r_data_control);	/* disable irq-generation */
+  sti(); 
+  irqs = probe_irq_on();
+  reset_cm260();		/* causes interrupt */
+  udelay(10);			/* wait for it */
+  irq = probe_irq_off(irqs);
+  outw(dc_normal | READ_AHEAD, r_data_control);	/* services interrupt */
+  if (nr && irq!=nr && irq>0) return 0;	/* wrong interrupt happened */
+  else return irq;
+}
+#endif
+
+#ifdef MODULE
+
+static int cm206[2] = {0,0};	/* for compatible `insmod' parameter passing */
+void parse_options(void) 
+{
+  int i;
+  for (i=0; i<2; i++) {
+    if (0x300 <= cm206[i] && i<= 0x370 && cm206[i] % 0x10 == 0) {
+      cm206_base = cm206[i];
+      auto_probe=0;
+    }
+    else if (3 <= cm206[i] && cm206[i] <= 15) {
+      cm206_irq = cm206[i];
+      auto_probe=0;
+    }
+  }
+}
+
+#define cm206_init init_module
+
+#endif MODULE
+
+
+int cm206_init(void)
+{
+  uch e=0;
+  long int size=sizeof(struct cm206_struct);
+
+  printk("cm206: v" VERSION);
+#if defined(MODULE) 
+  parse_options();
+#if !defined(AUTO_PROBE_MODULE)
+   auto_probe=0;
+#endif
+#endif
+  cm206_base = probe_base_port(auto_probe ? 0 : cm206_base);
+  if (!cm206_base) {
+    printk(" can't find adapter!\n");
+    return -EIO;
+  }
+  printk(" adapter at 0x%x", cm206_base);
+  request_region(cm206_base, 16, "cm206");
+  cd = (struct cm206_struct *) kmalloc(size, GFP_KERNEL);
+  if (!cd) return -EIO;
+  /* Now we have found the adaptor card, try to reset it. As we have
+   * found out earlier, this process generates an interrupt as well,
+   * so we might just exploit that fact for irq probing! */
+#if !defined(MODULE) || defined(AUTO_PROBE_MODULE)
+  cm206_irq = probe_irq(auto_probe ? 0 : cm206_irq);	
+  if (cm206_irq<=0) {
+    printk("can't find IRQ!\n");
+    cleanup(1);
+    return -EIO;
+  }
+  else printk(" IRQ %d found\n", cm206_irq);
+#else
+  reset_cm260();
+  printk(" using IRQ %d\n", cm206_irq);
+#endif
+  if (send_receive_polled(c_drive_configuration) != c_drive_configuration) 
+    {
+      printk(" drive not there\n");
+      cleanup(1);
+      return -EIO;
+    }
+  e = send_receive_polled(c_gimme);
+  printk("Firmware revision %d", e & dcf_revision_code);
+  if (e & dcf_transfer_rate) printk(" double");
+  else printk(" single");
+  printk(" speed drive");
+  if (e & dcf_motorized_tray) printk(", motorized tray");
+  if (request_irq(cm206_irq, cm206_interrupt, 0, "cm206")) {
+    printk("\nUnable to reserve IRQ---aborted\n");
+    cleanup(2);
+    return -EIO;
+  }
+  printk(".\n");
+  if (register_blkdev(MAJOR_NR, "cm206", &cm206_fops) != 0) {
+    printk("Cannot register for major %d!\n", MAJOR_NR);
+    cleanup(3);
+    return -EIO;
+  }
+  blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+  read_ahead[MAJOR_NR] = 16;	/* reads ahead what? */
+  bh_base[CM206_BH].routine = cm206_bh;
+  enable_bh(CM206_BH);
+
+  memset(cd, 0, sizeof(*cd));	/* give'm some reasonable value */
+  cd->sector_last = -1;		/* flag no data buffered */
+  cd->adapter_last = -1;
+  cd->timer.function = cm206_timeout;
+  cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97;
+  printk("%d kB adapter memory available, "  
+	 " %ld bytes kernel memory used.\n", cd->max_sectors*2, size);
+  return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+  cleanup(4);
+  printk("cm206 removed\n");
+}
+      
+#else MODULE
+
+/* This setup function accepts either `auto' or numbers in the range
+ * 3--11 (for irq) or 0x300--0x370 (for base port) or both. */
+void cm206_setup(char *s, int *p)
+{
+  int i;
+  if (!strcmp(s, "auto")) auto_probe=1;
+  for(i=1; i<=p[0]; i++) {
+    if (0x300 <= p[i] && i<= 0x370 && p[i] % 0x10 == 0) {
+      cm206_base = p[i];
+      auto_probe = 0;
+    }
+    else if (3 <= p[i] && p[i] <= 15) {
+      cm206_irq = p[i];
+      auto_probe = 0;
+    }
+  }
+}
+#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/gscd.c linux/drivers/cdrom/gscd.c
--- v1.3.35/linux/drivers/cdrom/gscd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/gscd.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,1126 @@
+#define GSCD_VERSION "0.4a Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>"
+
+/*
+	linux/drivers/block/gscd.c - GoldStar R420 CDROM driver
+
+        Copyright (C) 1995  Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
+        based upon pre-works by   Eberhard Moenkeberg <emoenke@gwdg.de>
+        
+
+        For all kind of other information about the GoldStar CDROM
+        and this Linux device driver I installed a WWW-URL:
+        http://linux.rz.fh-hannover.de/~raupach        
+
+
+             If you are the editor of a Linux CD, you should
+             enable gscd.c within your boot floppy kernel and
+             send me one of your CDs for free.
+
+
+        --------------------------------------------------------------------
+	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; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* These settings are for various debug-level. Leave they untouched ... */ 
+#define  NO_GSCD_DEBUG 
+#define  NO_IOCTL_DEBUG
+#define  NO_MODULE_DEBUG
+#define  NO_FUTURE_WORK
+/*------------------------*/
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/malloc.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[] = UTS_RELEASE;
+#endif
+#endif MODULE
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR GOLDSTAR_CDROM_MAJOR
+#include <linux/blk.h>
+#define gscd_port gscd /* for compatible parameter passing with "insmod" */
+#include <linux/gscd.h>
+
+
+static int gscdPresent            = 0;
+
+static unsigned char gscd_buf[2048];    /* buffer for block size conversion */
+static int   gscd_bn              = -1;
+static short gscd_port            = GSCD_BASE_ADDR;
+
+/* Kommt spaeter vielleicht noch mal dran ...
+ *    static struct wait_queue *gscd_waitq = NULL;
+ */ 
+
+static void gscd_transfer         (void);
+static void gscd_read_cmd         (void);
+static void gscd_hsg2msf          (long hsg, struct msf *msf);
+static void gscd_bin2bcd          (unsigned char *p);
+
+/* Schnittstellen zum Kern/FS */
+
+static void do_gscd_request       (void);
+static int  gscd_ioctl            (struct inode *, struct file *, unsigned int, unsigned long);
+static int  gscd_open             (struct inode *, struct file *);
+static void gscd_release          (struct inode *, struct file *);
+static int  check_gscd_med_chg    (kdev_t);
+
+/*      GoldStar Funktionen    */
+
+static void cc_Reset             (void);
+static int  wait_drv_ready       (void);
+static int  find_drives          (void);
+static void cmd_out              (int, char *, char *, int);
+static void cmd_status           (void);
+static void cc_Ident             (char *);
+static void cc_SetSpeed          (void);
+static void init_cd_drive        (int);
+
+static int  get_status           (void);
+static void clear_Audio          (void);
+static void cc_invalidate        (void);
+
+/* some things for the next version */
+#ifdef FUTURE_WORK
+static void update_state          (void);
+static long gscd_msf2hsg          (struct msf *mp);
+static int  gscd_bcd2bin          (unsigned char bcd);
+#endif
+
+/*    common GoldStar Initialization    */
+
+static int my_gscd_init (void);
+
+
+/*      lo-level cmd-Funktionen    */
+
+static void cmd_info_in          ( char *, int );
+static void cmd_end              ( void );
+static void cmd_read_b           ( char *, int, int );
+static void cmd_read_w           ( char *, int, int );
+static int  cmd_unit_alive       ( void );
+static void cmd_write_cmd        ( char * );
+
+
+/*      GoldStar Variablen     */
+
+static int  curr_drv_state;
+static int  drv_states[]       = {0,0,0,0,0,0,0,0};
+static int  drv_mode;
+static int  disk_state;
+static int  speed;
+static int  ndrives;
+
+static unsigned char drv_num_read;
+static unsigned char f_dsk_valid;
+static unsigned char current_drive;
+static unsigned char f_drv_ok;
+
+
+static char f_AudioPlay;
+static char f_AudioPause;
+static int  AudioStart_m;
+static int  AudioStart_f;
+static int  AudioEnd_m;
+static int  AudioEnd_f;
+ 
+
+static struct file_operations gscd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	gscd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	gscd_open,		/* open */
+	gscd_release,		/* release */
+	NULL,                   /* fsync */
+	NULL,                   /* fasync*/
+	check_gscd_med_chg,     /* media change */
+	NULL                    /* revalidate */
+};
+
+/* 
+ * Checking if the media has been changed
+ * (not yet implemented)
+ */
+static int check_gscd_med_chg (kdev_t full_dev)
+{
+   int target;
+
+
+   target = MINOR(full_dev);
+
+   if (target > 0) 
+   {
+      printk("GSCD: GoldStar CD-ROM request error: invalid device.\n");
+      return 0;
+   }
+
+   #ifdef GSCD_DEBUG
+   printk ("gscd: check_med_change\n");
+   #endif
+ 
+  return 0;
+}
+
+
+void gscd_setup (char *str, int *ints)
+{
+  if (ints[0] > 0) 
+  {
+     gscd_port = ints[1];
+  }
+}
+
+
+static int gscd_ioctl (struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{
+unsigned char to_do[10];
+unsigned char dummy;
+
+                             
+    switch (cmd)
+    {
+       case CDROMSTART:     /* Spin up the drive */
+		/* Don't think we can do this.  Even if we could,
+ 		 * I think the drive times out and stops after a while
+		 * anyway.  For now, ignore it.
+		 */
+            return 0;
+
+       case CDROMRESUME:   /* keine Ahnung was das ist */
+            return 0;
+
+
+       case CDROMEJECT:
+            cmd_status ();
+            to_do[0] = CMD_TRAY_CTL;
+            cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+
+            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.
+ */
+
+static void gscd_transfer (void)
+{
+long offs;
+
+	while (CURRENT -> nr_sectors > 0 && gscd_bn == CURRENT -> sector / 4)
+	{
+		offs = (CURRENT -> sector & 3) * 512;
+		memcpy(CURRENT -> buffer, gscd_buf + offs, 512);
+		CURRENT -> nr_sectors--;
+		CURRENT -> sector++;
+		CURRENT -> buffer += 512;
+	}
+}
+
+
+/*
+ * I/O request routine called from Linux kernel.
+ */
+
+static void do_gscd_request (void)
+{
+unsigned int block,dev;
+unsigned int nsect;
+
+repeat:
+	if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) return;
+	INIT_REQUEST;
+	dev = MINOR(CURRENT->rq_dev);
+	block = CURRENT->sector;
+	nsect = CURRENT->nr_sectors;
+
+	if (CURRENT == NULL || CURRENT -> sector == -1)
+		return;
+
+	if (CURRENT -> cmd != READ)
+	{
+		printk("GSCD: bad cmd %d\n", CURRENT -> cmd);
+		end_request(0);
+		goto repeat;
+	}
+
+	if (MINOR(CURRENT -> rq_dev) != 0)
+	{
+		printk("GSCD: this version supports only one device\n");
+		end_request(0);
+		goto repeat;
+	}
+
+	gscd_transfer();
+
+	/* if we satisfied the request from the buffer, we're done. */
+
+	if (CURRENT -> nr_sectors == 0)
+	{
+		end_request(1);
+		goto repeat;
+	}
+
+#ifdef GSCD_DEBUG
+        printk ("GSCD: dev %d, block %d, nsect %d\n", dev, block, nsect );
+#endif
+
+	gscd_read_cmd ();
+}
+
+
+
+/*
+ * Check the result of the set-mode command.  On success, send the
+ * read-data command.
+ */
+
+static void
+gscd_read_cmd (void)
+{
+long   block;
+struct gscd_Play_msf gscdcmd;
+char   cmd[] = { CMD_READ, 0x80, 0,0,0, 0,1 }; /* cmd mode M-S-F secth sectl */
+
+
+
+        cmd_status ();
+        if ( disk_state & (ST_NO_DISK | ST_DOOR_OPEN) )
+        {
+           printk ( "GSCD: no disk or door open\n" );
+           end_request (0);
+        }
+        else
+        {
+           if ( disk_state & ST_INVALID )
+           {
+              printk ( "GSCD: disk invalid\n" );
+              end_request (0);
+           }
+           else
+           {
+              gscd_bn = -1;		/* purge our buffer */
+              block = CURRENT -> sector / 4;
+              gscd_hsg2msf(block, &gscdcmd.start);	/* cvt to msf format */
+
+              cmd[2] = gscdcmd.start.min;
+              cmd[3] = gscdcmd.start.sec;
+              cmd[4] = gscdcmd.start.frame;
+
+#ifdef GSCD_DEBUG
+              printk ("GSCD: read msf %d:%d:%d\n", cmd[2], cmd[3], cmd[4] ); 
+#endif 
+              cmd_out ( TYPE_DATA, (char *)&cmd, (char *)&gscd_buf[0], 1 );
+        
+              gscd_bn = CURRENT -> sector / 4;
+              gscd_transfer();
+              end_request(1);
+	   }
+	}
+	SET_TIMER(do_gscd_request, 1);
+}
+
+
+/*
+ * Open the device special file.  Check that a disk is in.
+ */
+
+static int gscd_open (struct inode *ip, struct file *fp)
+{
+int   st;
+
+#ifdef GSCD_DEBUG
+printk ( "GSCD: open\n" );
+#endif
+
+	if (gscdPresent == 0)
+		return -ENXIO;			/* no hardware */
+
+        get_status ();
+        st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN);
+        if ( st )
+        {
+           printk ( "GSCD: no disk or door open\n" );
+           return -ENXIO;
+        }
+                   
+/*	if (updateToc() < 0)
+		return -EIO;
+*/
+
+        #ifdef MODULE
+           MOD_INC_USE_COUNT;
+        #endif
+ 
+	return 0;
+}
+
+
+/*
+ * On close, we flush all gscd blocks from the buffer cache.
+ */
+
+static void gscd_release (struct inode * inode, struct file * file)
+{
+
+#ifdef GSCD_DEBUG
+printk ( "GSCD: release\n" );
+#endif
+
+	gscd_bn = -1;
+	sync_dev(inode->i_rdev);
+	invalidate_buffers(inode -> i_rdev);
+
+        #ifdef MODULE
+           MOD_DEC_USE_COUNT;
+        #endif
+}
+
+
+int get_status (void)
+{
+int  status;
+
+    cmd_status ();
+    status = disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01);
+     
+    if ( status == (ST_x08 | ST_x04 | ST_INVALID | ST_x01) )
+    {
+       cc_invalidate ();
+       return 1;
+    }
+    else
+    {
+       return 0;
+    }
+}
+
+
+void cc_invalidate (void)
+{
+   drv_num_read  = 0xFF;
+   f_dsk_valid   = 0xFF;
+   current_drive = 0xFF;
+   f_drv_ok      = 0xFF;
+
+   clear_Audio ();
+   
+}   
+
+void clear_Audio (void)
+{
+
+   f_AudioPlay = 0;
+   f_AudioPause = 0;
+   AudioStart_m = 0;
+   AudioStart_f = 0;
+   AudioEnd_m   = 0;
+   AudioEnd_f   = 0;
+   
+}
+
+/*
+ *   waiting ?  
+ */
+
+int wait_drv_ready (void)
+{
+int found, read;
+
+   do
+   {  
+       found = inb ( GSCDPORT(0) ); 
+       found &= 0x0f;
+       read  = inb ( GSCDPORT(0) );
+       read  &= 0x0f;
+   } while ( read != found );
+   
+#ifdef GSCD_DEBUG
+printk ( "Wait for: %d\n", read );
+#endif   
+   
+   return read;
+}
+
+void cc_Ident (char * respons)
+{
+char to_do [] = {CMD_IDENT, 0, 0};
+
+    cmd_out (TYPE_INFO, (char *)&to_do, (char *)respons, (int)0x1E );
+
+}
+
+void cc_SetSpeed (void)
+{
+char to_do [] = {CMD_SETSPEED, 0, 0};
+char dummy;
+
+    if ( speed > 0 )
+    {
+       to_do[1] = speed & 0x0F;
+       cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+    }
+}
+
+
+void cc_Reset (void)
+{
+char to_do [] = {CMD_RESET, 0};
+char dummy;
+
+   cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+}
+
+
+
+void cmd_status (void)
+{
+char to_do [] = {CMD_STATUS, 0};
+char dummy;
+
+   cmd_out (TYPE_INFO, (char *)&to_do, (char *)&dummy, 0);
+
+#ifdef GSCD_DEBUG
+printk ("GSCD: Status: %d\n", disk_state );
+#endif
+
+}
+
+void cmd_out ( int cmd_type, char * cmd, char * respo_buf, int respo_count )
+{
+int        result;
+
+
+       result = wait_drv_ready ();
+       if ( result != drv_mode )
+       {
+       unsigned long test_loops = 0xFFFF;
+       int           i,dummy;
+
+          outb ( curr_drv_state, GSCDPORT(0));
+          
+          /* LOCLOOP_170 */
+          do
+          {
+             result = wait_drv_ready ();
+             test_loops--;
+          } while ( (result != drv_mode) && (test_loops > 0) );
+
+          if ( result != drv_mode )
+          {
+             disk_state = ST_x08 | ST_x04 | ST_INVALID;
+             return;
+          }          
+        
+          /* ...and waiting */
+          for ( i=1,dummy=1 ; i<0xFFFF ; i++ )
+          {
+             dummy *= i;
+          }              
+       }
+
+       /* LOC_172 */    
+       /* check the unit */
+       /* and wake it up */
+       if ( cmd_unit_alive () != 0x08 )
+       {
+          /* LOC_174 */
+          /* game over for this unit */
+          disk_state = ST_x08 | ST_x04 | ST_INVALID;
+          return;
+       }
+       
+       /* LOC_176 */
+       #ifdef GSCD_DEBUG
+        printk ("LOC_176 ");
+       #endif       
+       if ( drv_mode == 0x09 )
+       {
+          /* magic... */
+          printk ("GSCD: magic ...\n");       
+          outb ( result, GSCDPORT(2));
+       }
+
+       /* write the command to the drive */
+       cmd_write_cmd (cmd);
+
+       /* LOC_178 */
+       for (;;)
+       {
+          result = wait_drv_ready ();
+          if ( result != drv_mode )
+          {
+             /* LOC_179 */
+             if ( result == 0x04 )                  /* Mode 4 */
+             {
+                /* LOC_205 */
+                #ifdef GSCD_DEBUG
+                printk ("LOC_205 ");	              
+                #endif
+                disk_state = inb ( GSCDPORT (2));
+             
+                do
+                {
+                   result = wait_drv_ready ();
+                } while ( result != drv_mode );
+                return;
+
+             }
+             else
+             {
+                if ( result == 0x06 )               /* Mode 6 */
+                {
+                   /* LOC_181 */
+                   #ifdef GSCD_DEBUG
+                    printk ("LOC_181 ");	              
+                   #endif
+
+                   if (cmd_type == TYPE_DATA)
+                   {
+                      /* read data */
+                      /* LOC_184 */
+                      if ( drv_mode == 9 )
+                      {
+                         /* read the data to the buffer (word) */
+ 
+                         /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */
+                         cmd_read_w ( respo_buf, respo_count, CD_FRAMESIZE/2 );
+                         return;
+                      } 
+                      else
+                      { 
+                         /* read the data to the buffer (byte) */
+                         
+                         /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW)    */
+                         cmd_read_b ( respo_buf, respo_count, CD_FRAMESIZE );
+                         return;
+                      }
+                   }
+                   else
+                   { 
+                      /* read the info to the buffer */
+                      cmd_info_in ( respo_buf, respo_count ); 
+                      return;
+                   }                  
+
+                   return;
+                }
+             }
+
+          }
+          else
+          {
+             disk_state = ST_x08 | ST_x04 | ST_INVALID;         
+             return;
+          }    
+       } /* for (;;) */
+
+
+#ifdef GSCD_DEBUG
+printk ("\n");       
+#endif    
+}
+
+
+static void cmd_write_cmd ( char *pstr )
+{
+int  i,j;
+
+    /* LOC_177 */
+    #ifdef GSCD_DEBUG
+     printk ("LOC_177 ");       
+    #endif
+       
+    /* calculate the number of parameter */
+    j = *pstr & 0x0F;
+
+    /* shift it out */
+    for ( i=0 ; i<j ; i++ )
+    {
+       outb ( *pstr, GSCDPORT(2) );
+       pstr++;
+    }
+}
+
+
+static int cmd_unit_alive ( void )
+{
+int            result;
+unsigned long  max_test_loops;
+
+
+    /* LOC_172 */
+    #ifdef GSCD_DEBUG
+     printk ("LOC_172 ");       
+    #endif
+
+    outb ( curr_drv_state, GSCDPORT(0));       
+    max_test_loops = 0xFFFF;
+     
+    do
+    {
+       result = wait_drv_ready ();
+       max_test_loops--;
+    } while ( (result != 0x08) && (max_test_loops > 0) );
+
+    return result;
+}       
+
+
+static void cmd_info_in ( char *pb, int count )
+{
+int   result;
+char  read;
+
+
+        /* read info */
+        /* LOC_182 */
+        #ifdef GSCD_DEBUG
+         printk ("LOC_182 ");				              
+        #endif
+       
+        do
+        {
+           read = inb (GSCDPORT(2)); 
+           if ( count > 0 )
+           {
+              *pb = read;
+              pb++;
+              count--;
+           }
+                         
+           /* LOC_183 */
+           do
+           {
+              result = wait_drv_ready ();
+           } while ( result == 0x0E );
+        } while ( result == 6 );                     
+
+        cmd_end ();
+        return;
+}
+
+
+static void cmd_read_b ( char *pb, int count, int size )
+{
+int  result;
+int  i;
+                     
+                     
+       /* LOC_188 */
+       /* LOC_189 */
+       #ifdef GSCD_DEBUG
+        printk ("LOC_189 ");			              
+       #endif
+
+       do 
+       {
+          do
+          {
+             result = wait_drv_ready ();
+          } while ( result != 6 || result == 0x0E );
+                         
+          if ( result != 6 )
+          {
+             cmd_end ();
+             return;
+          }                       
+          
+          #ifdef GSCD_DEBUG
+           printk ("LOC_191 ");       
+          #endif
+
+          for ( i=0 ; i< size ; i++ )
+          {
+             *pb = inb (GSCDPORT(2));
+             pb++;
+          }
+          count--;
+       } while ( count > 0 );
+       
+       cmd_end ();
+       return;
+}
+
+
+static void cmd_end (void)
+{
+int   result;
+
+
+      /* LOC_204 */
+      #ifdef GSCD_DEBUG
+       printk ("LOC_204 ");				              
+      #endif
+      
+      do
+      {
+         result = wait_drv_ready ();
+         if ( result == drv_mode )
+         {
+            return;
+         }
+      } while ( result != 4 );
+
+      /* LOC_205 */
+      #ifdef GSCD_DEBUG
+       printk ("LOC_205 ");			              
+      #endif
+       
+      disk_state = inb ( GSCDPORT (2));
+             
+      do
+      {
+         result = wait_drv_ready ();
+      } while ( result != drv_mode );
+      return;
+
+}
+
+
+static void cmd_read_w ( char *pb, int count, int size )
+{
+int  result;
+int  i;
+
+
+    #ifdef GSCD_DEBUG
+     printk ("LOC_185 ");			              
+    #endif
+ 
+    do 
+    {
+       /* LOC_185 */
+       do
+       {
+          result = wait_drv_ready ();
+       } while ( result != 6 || result == 0x0E );
+                         
+       if ( result != 6 )
+       {
+          cmd_end ();
+          return;
+       }                       
+                         
+       for ( i=0 ; i<size ; i++ )
+       {
+           /* na, hier muss ich noch mal drueber nachdenken */
+           *pb = inw(GSCDPORT(2));
+           pb++;
+        }
+        count--;
+     } while ( count > 0 );
+                    
+     cmd_end ();
+     return;
+}
+
+int find_drives (void)
+{
+int *pdrv;
+int drvnum;
+int subdrv;
+int i;
+
+    speed           = 0;
+    pdrv            = (int *)&drv_states;
+    curr_drv_state  = 0xFE;
+    subdrv          = 0;
+    drvnum          = 0;
+
+    for ( i=0 ; i<8 ; i++ )
+    { 
+       subdrv++;
+       cmd_status ();
+       disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01;
+       if ( disk_state != (ST_x08 | ST_x04 | ST_INVALID) )
+       {
+          /* LOC_240 */
+          *pdrv = curr_drv_state;
+          init_cd_drive (drvnum);
+          pdrv++;
+          drvnum++;
+       }
+       else
+       {
+          if ( subdrv < 2 )
+          {
+             continue;
+          }
+          else
+          {
+             subdrv = 0;
+          }
+       }   
+
+/*       curr_drv_state<<1;         <-- das geht irgendwie nicht */ 
+/* muss heissen:    curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */
+         curr_drv_state *= 2;
+         curr_drv_state |= 1;
+#ifdef GSCD_DEBUG
+         printk ("DriveState: %d\n", curr_drv_state );
+#endif
+    } 
+
+    ndrives = drvnum;
+    return drvnum;
+}    
+
+void init_cd_drive ( int num )
+{
+char resp [50];
+int  i;
+
+   printk ("GSCD: init unit %d\n", num );
+   cc_Ident ((char *)&resp);
+
+   printk ("GSCD: identification: ");
+   for ( i=0 ; i<0x1E; i++ )
+   {
+      printk ( "%c", resp[i] );
+   }
+   printk ("\n");
+   
+   cc_SetSpeed ();  
+
+}    
+
+#ifdef FUTURE_WORK
+/* return_done */
+static void update_state ( void )
+{
+unsigned int AX;
+
+
+    if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 )
+    {
+       if ( disk_state == (ST_x08 | ST_x04 | ST_INVALID))
+       {
+          AX = ST_INVALID;
+       }
+  
+       if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 )
+       {
+          invalidate ();
+          f_drv_ok = 0;
+       }
+       
+       AX |= 0x8000;
+    }
+    
+    if ( disk_state & ST_PLAYING )
+    {
+       AX |= 0x200;
+    }
+    
+    AX |= 0x100;
+    /* pkt_esbx = AX; */
+
+    disk_state = 0;
+    
+}
+#endif
+
+/* Init for the Module-Version */
+int init_module (void)
+{
+long err;
+
+
+     /* call the GoldStar-init */
+     err = my_gscd_init ( );
+
+     if ( err < 0 )
+     {
+         return err;
+     }
+     else
+     {
+        printk ( "Happy GoldStar !\n" );
+        return 0;
+     }    
+}
+
+#ifdef MODULE
+void cleanup_module (void)
+{
+
+   if (MOD_IN_USE)
+   {
+      printk("GoldStar-module in use - can't remove it.\n" );
+      return;
+   }
+ 
+   if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL))
+   {
+      printk("What's that: can't unregister GoldStar-module\n" );
+      return;
+   }
+
+   release_region (gscd_port,4);
+   printk( "GoldStar-module released.\n" );
+}
+#endif
+
+
+/* Test for presence of drive and initialize it.  Called only at boot time. */
+int gscd_init (void)
+{
+   return my_gscd_init ();
+}
+
+
+/* This is the common initalisation for the GoldStar drive. */
+/* It is called at boot time AND for module init.           */
+int my_gscd_init (void)
+{
+int i;
+int result;
+
+        printk ("GSCD: version %s\n", GSCD_VERSION);
+        printk ("GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", gscd_port);
+
+        if (check_region(gscd_port, 4)) 
+        {
+	  printk("GSCD: Init failed, I/O port (%X) already in use.\n", gscd_port);
+	  return -EIO;
+	}
+	  
+
+	/* check for card */
+	result = wait_drv_ready ();
+        if ( result == 0x09 )
+        {
+           printk ("GSCD: DMA kann ich noch nicht!\n" );
+           return -EIO;
+        }
+ 
+        if ( result == 0x0b )
+        {
+           drv_mode = result;
+           i = find_drives ();
+           if ( i == 0 )
+           {
+              printk ( "GSCD: GoldStar CD-ROM Drive is not found.\n" );
+              return -EIO;
+           }
+        }
+
+        if ( (result != 0x0b) && (result != 0x09) )
+        {
+              printk ("GSCD: GoldStar Interface Adapter does not exist or H/W error\n" );
+              return -EIO;
+        }          		            
+
+        /* reset all drives */
+        i = 0;
+        while ( drv_states[i] != 0 )
+        {
+           curr_drv_state = drv_states[i];
+           printk ( "GSCD: Reset unit %d ... ",i );
+           cc_Reset ();
+           printk ( "done\n" );
+           i++;
+        }
+
+	if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0)
+	{
+		printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n",
+		       MAJOR_NR);
+		return -EIO;
+	}
+
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+        
+        disk_state = 0;
+        gscdPresent = 1;
+
+	request_region(gscd_port, 4, "gscd");
+
+        printk ( "GSCD: GoldStar CD-ROM Drive found.\n" );
+	return 0;
+}
+
+static void gscd_hsg2msf (long hsg, struct msf *msf)
+{
+	hsg += CD_BLOCK_OFFSET;
+	msf -> min = hsg / (CD_FRAMES*CD_SECS);
+	hsg %= CD_FRAMES*CD_SECS;
+	msf -> sec = hsg / CD_FRAMES;
+	msf -> frame = hsg % CD_FRAMES;
+
+	gscd_bin2bcd(&msf -> min);		/* convert to BCD */
+	gscd_bin2bcd(&msf -> sec);
+	gscd_bin2bcd(&msf -> frame);
+}
+ 
+
+static void gscd_bin2bcd (unsigned char *p)
+{
+int u, t;
+
+	u = *p % 10;
+	t = *p / 10;
+	*p = u | (t << 4);
+}
+
+
+#ifdef FUTURE_WOTK
+static long gscd_msf2hsg (struct msf *mp)
+{
+	return gscd_bcd2bin(mp -> frame)
+		+ gscd_bcd2bin(mp -> sec) * CD_FRAMES
+		+ gscd_bcd2bin(mp -> min) * CD_FRAMES * CD_SECS
+		- CD_BLOCK_OFFSET;
+}
+
+static int gscd_bcd2bin (unsigned char bcd)
+{
+	return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+#endif
+
+
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
--- v1.3.35/linux/drivers/cdrom/mcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/mcd.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,1632 @@
+/*
+	linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
+
+	Copyright (C) 1992  Martin Harriss
+
+	martin@bdsi.com (no longer valid - where are you now, Martin?)
+
+	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; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	HISTORY
+
+	0.1	First attempt - internal use only
+	0.2	Cleaned up delays and use of timer - alpha release
+	0.3	Audio support added
+	0.3.1 Changes for mitsumi CRMC LU005S march version
+		   (stud11@cc4.kuleuven.ac.be)
+        0.3.2 bug fixes to the ioctls and merged with ALPHA0.99-pl12
+		   (Jon Tombs <jon@robots.ox.ac.uk>)
+        0.3.3 Added more #defines and mcd_setup()
+   		   (Jon Tombs <jon@gtex02.us.es>)
+
+	October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
+	Braunschweig, Germany: rework to speed up data read operation.
+	Also enabled definition of irq and address from bootstrap, using the
+	environment.
+	November 93 added code for FX001 S,D (single & double speed).
+	February 94 added code for broken M 5/6 series of 16-bit single speed.
+
+
+        0.4   
+        Added support for loadable MODULEs, so mcd can now also be loaded by 
+        insmod and removed by rmmod during runtime.
+        Werner Zimmermann (zimmerma@rz.fht-esslingen.de), Mar. 26, 95
+
+	0.5
+	I added code for FX001 D to drop from double speed to single speed 
+	when encountering errors... this helps with some "problematic" CD's
+	that are supposedly "OUT OF TOLERANCE" (but are really shitty presses!)
+	severly scratched, or possibly slightly warped! I have noticed that
+	the Mitsumi 2x/4x drives are just less tolerant and the firmware is 
+	not smart enough to drop speed,	so let's just kludge it with software!
+	****** THE 4X SPEED MITSUMI DRIVES HAVE THE SAME PROBLEM!!!!!! ******
+	Anyone want to "DONATE" one to me?! ;) I hear sometimes they are
+	even WORSE! ;)
+	** HINT... HINT... TAKE NOTES MITSUMI This could save some hassels with
+	certain "large" CD's that have data on the outside edge in your 
+	DOS DRIVERS .... Accuracy counts... speed is secondary ;)
+	17 June 95 Modifications By Andrew J. Kroll <ag784@freenet.buffalo.edu>
+	07 July 1995 Modifications by Andrew J. Kroll
+
+*/
+
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+    char kernel_version[]= UTS_RELEASE;
+# endif
+#define mcd_init init_module
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+/* #define REALLY_SLOW_IO  */
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR MITSUMI_CDROM_MAJOR
+#include <linux/blk.h>
+#define mcd_port mcd    /* for compatible parameter passing with "insmod" */
+#include <linux/mcd.h>
+
+#if 0
+static int mcd_sizes[] = { 0 };
+#endif
+
+/* 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; 
+
+/* How many sectors to hold at 1x speed counter */
+static int mcd1xhold = 0;
+
+/* Is the drive connected properly and responding?? */
+static int mcdPresent = 0;
+
+#if 0
+#define TEST1 /* <int-..> */
+#define TEST2 /* do_mcd_req */
+#define TEST3 */ /* MCD_S_state */
+#define TEST4 /* QUICK_LOOP-counter */
+#define TEST5 */ /* port(1) state */
+#endif
+
+#if 1
+#define QUICK_LOOP_DELAY udelay(45)  /* use udelay */
+#define QUICK_LOOP_COUNT 20
+#else
+#define QUICK_LOOP_DELAY
+#define QUICK_LOOP_COUNT 140 /* better wait constant time */
+#endif
+/* #define DOUBLE_QUICK_ONLY */
+
+#define CURRENT_VALID \
+  (CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
+   && CURRENT -> sector != -1)
+#define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
+#define MCD_BUF_SIZ 16
+static volatile int mcd_transfer_is_active;
+static char mcd_buf[2048*MCD_BUF_SIZ];	/* buffer for block size conversion */
+static volatile int mcd_buf_bn[MCD_BUF_SIZ], mcd_next_bn;
+static volatile int mcd_buf_in, mcd_buf_out = -1;
+static volatile int mcd_error;
+static int mcd_open_count;
+enum mcd_state_e {
+  MCD_S_IDLE,   /* 0 */
+  MCD_S_START,  /* 1 */
+  MCD_S_MODE, /* 2 */
+  MCD_S_READ,   /* 3 */
+  MCD_S_DATA,   /* 4 */
+  MCD_S_STOP,   /* 5 */
+  MCD_S_STOPPING /* 6 */
+};
+static volatile enum mcd_state_e mcd_state = MCD_S_IDLE;
+static int mcd_mode = -1;
+static int MCMD_DATA_READ= MCMD_PLAY_READ;
+#define READ_TIMEOUT 3000
+#define WORK_AROUND_MITSUMI_BUG_92
+#define WORK_AROUND_MITSUMI_BUG_93
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+int mitsumi_bug_93_wait = 0;
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+static short mcd_port = MCD_BASE_ADDR; /* used as "mcd" by "insmod" */
+static int   mcd_irq  = MCD_INTR_NR; /* must directly follow mcd_port */
+
+static int McdTimeout, McdTries;
+static struct wait_queue *mcd_waitq = NULL;
+
+static struct mcd_DiskInfo DiskInfo;
+static struct mcd_Toc Toc[MAX_TRACKS];
+static struct mcd_Play_msf mcd_Play;
+
+static int audioStatus;
+static char mcdDiskChanged;
+static char tocUpToDate;
+static char mcdVersion;
+
+static void mcd_transfer(void);
+static void mcd_poll(void);
+static void mcd_invalidate_buffers(void);
+static void hsg2msf(long hsg, struct msf *msf);
+static void bin2bcd(unsigned char *p);
+static int bcd2bin(unsigned char bcd);
+static int mcdStatus(void);
+static void sendMcdCmd(int cmd, struct mcd_Play_msf *params);
+static int getMcdStatus(int timeout);
+static int GetQChannelInfo(struct mcd_Toc *qp);
+static int updateToc(void);
+static int GetDiskInfo(void);
+static int GetToc(void);
+static int getValue(unsigned char *result);
+
+
+void mcd_setup(char *str, int *ints)
+{
+   if (ints[0] > 0)
+      mcd_port = ints[1];
+   if (ints[0] > 1)      
+      mcd_irq  = ints[2];
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+   if (ints[0] > 2)
+      mitsumi_bug_93_wait = ints[3];
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+}
+
+ 
+static int
+check_mcd_change(kdev_t full_dev)
+{
+   int retval, target;
+
+
+#if 1	 /* the below is not reliable */
+   return 0;
+#endif  
+   target = MINOR(full_dev);
+
+   if (target > 0) {
+      printk("mcd: Mitsumi CD-ROM request error: invalid device.\n");
+      return 0;
+   }
+
+   retval = mcdDiskChanged;
+   mcdDiskChanged = 0;
+
+   return retval;
+}
+
+
+/*
+ * Do a 'get status' command and get the result.  Only use from the top half
+ * because it calls 'getMcdStatus' which sleeps.
+ */
+
+static int
+statusCmd(void)
+{
+	int st, retry;
+
+	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
+	{
+
+		outb(MCMD_GET_STATUS, MCDPORT(0));	/* send get-status cmd */
+		st = getMcdStatus(MCD_STATUS_DELAY);
+		if (st != -1)
+			break;
+	}
+
+	return st;
+}
+
+
+/*
+ * Send a 'Play' command and get the status.  Use only from the top half.
+ */
+
+static int
+mcdPlay(struct mcd_Play_msf *arg)
+{
+	int retry, st;
+
+	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
+	{
+		sendMcdCmd(MCMD_PLAY_READ, arg);
+		st = getMcdStatus(2 * MCD_STATUS_DELAY);
+		if (st != -1)
+			break;
+	}
+
+	return st;
+}
+
+
+long
+msf2hsg(struct msf *mp)
+{
+	return bcd2bin(mp -> frame)
+		+ bcd2bin(mp -> sec) * 75
+		+ bcd2bin(mp -> min) * 4500
+		- 150;
+}
+
+
+static int
+mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
+						unsigned long 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 mcd_Toc *tocPtr;
+	struct cdrom_subchnl subchnl;
+	struct cdrom_volctrl volctrl;
+
+	if (!ip)
+		return -EINVAL;
+
+	st = statusCmd();
+	if (st < 0)
+		return -EIO;
+
+	if (!tocUpToDate)
+	{
+		i = updateToc();
+		if (i < 0)
+			return i;	/* error reading TOC */
+	}
+
+	switch (cmd)
+	{
+	case CDROMSTART:     /* Spin up the drive */
+		/* Don't think we can do this.  Even if we could,
+ 		 * I think the drive times out and stops after a while
+		 * anyway.  For now, ignore it.
+		 */
+
+		return 0;
+
+	case CDROMSTOP:      /* Spin down the drive */
+		outb(MCMD_STOP, MCDPORT(0));
+		i = getMcdStatus(MCD_STATUS_DELAY);
+
+		/* should we do anything if it fails? */
+
+		audioStatus = CDROM_AUDIO_NO_STATUS;
+		return 0;
+
+	case CDROMPAUSE:     /* Pause the drive */
+		if (audioStatus != CDROM_AUDIO_PLAY)
+			return -EINVAL;
+
+		outb(MCMD_STOP, MCDPORT(0));
+		i = getMcdStatus(MCD_STATUS_DELAY);
+
+		if (GetQChannelInfo(&qInfo) < 0)
+		{
+			/* didn't get q channel info */
+
+			audioStatus = CDROM_AUDIO_NO_STATUS;
+			return 0;
+		}
+
+		mcd_Play.start = qInfo.diskTime;	/* remember restart point */
+
+		audioStatus = CDROM_AUDIO_PAUSED;
+		return 0;
+
+	case CDROMRESUME:    /* Play it again, Sam */
+		if (audioStatus != CDROM_AUDIO_PAUSED)
+			return -EINVAL;
+
+		/* restart the drive at the saved position. */
+
+		i = mcdPlay(&mcd_Play);
+		if (i < 0)
+		{
+			audioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+
+		audioStatus = CDROM_AUDIO_PLAY;
+		return 0;
+
+	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
+		if (st)
+			return st;
+
+		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+
+		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;
+
+		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",
+	mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
+	mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
+#endif
+
+		i = mcdPlay(&mcd_Play);
+		if (i < 0)
+		{
+			audioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+
+		audioStatus = CDROM_AUDIO_PLAY;
+		return 0;
+
+	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
+
+		if (audioStatus == CDROM_AUDIO_PLAY) {
+		  outb(MCMD_STOP, MCDPORT(0));
+		  i = getMcdStatus(MCD_STATUS_DELAY);
+		  audioStatus = CDROM_AUDIO_NO_STATUS;
+		}
+
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+		if (st)
+			return st;
+
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+
+		/* 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;
+
+#ifdef MCD_DEBUG
+printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
+mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
+#endif
+
+		i = mcdPlay(&mcd_Play);
+		if (i < 0)
+		{
+			audioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+
+		audioStatus = CDROM_AUDIO_PLAY;
+		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;
+		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+		return 0;
+
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
+		if (st)
+			return st;
+
+		memcpy_fromfs(&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)
+			return -EINVAL;
+
+		else
+			tocPtr = &Toc[entry.cdte_track];
+
+		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);
+
+		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);
+		}
+
+		else
+			return -EINVAL;
+
+		memcpy_tofs((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;
+
+		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
+
+		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;
+
+		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
+		return 0;
+
+	case CDROMVOLCTRL:   /* Volume control */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
+		if (st)
+			return st;
+
+		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+		outb(MCMD_SET_VOLUME, MCDPORT(0));
+		outb(volctrl.channel0, MCDPORT(0));
+		outb(255, MCDPORT(0));
+		outb(volctrl.channel1, MCDPORT(0));
+		outb(255, MCDPORT(0));
+
+		i = getMcdStatus(MCD_STATUS_DELAY);
+		if (i < 0)
+			return -EIO;
+
+		{
+			char a, b, c, d;
+
+			getValue(&a);
+			getValue(&b);
+			getValue(&c);
+			getValue(&d);
+		}
+
+		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.
+ */
+
+static void
+mcd_transfer(void)
+{
+  if (CURRENT_VALID) {
+    while (CURRENT -> nr_sectors) {
+      int bn = CURRENT -> sector / 4;
+      int i;
+      for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn; ++i)
+	;
+      if (i < MCD_BUF_SIZ) {
+	int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+	int nr_sectors = 4 - (CURRENT -> sector & 3);
+	if (mcd_buf_out != i) {
+	  mcd_buf_out = i;
+	  if (mcd_buf_bn[i] != bn) {
+	    mcd_buf_out = -1;
+	    continue;
+	  }
+	}
+	if (nr_sectors > CURRENT -> nr_sectors)
+	  nr_sectors = CURRENT -> nr_sectors;
+	memcpy(CURRENT -> buffer, mcd_buf + offs, nr_sectors * 512);
+	CURRENT -> nr_sectors -= nr_sectors;
+	CURRENT -> sector += nr_sectors;
+	CURRENT -> buffer += nr_sectors * 512;
+      } else {
+	mcd_buf_out = -1;
+	break;
+      }
+    }
+  }
+}
+
+
+/*
+ * We only seem to get interrupts after an error.
+ * Just take the interrupt and clear out the status reg.
+ */
+
+static void
+mcd_interrupt(int irq, struct pt_regs * regs)
+{
+	int st;
+
+	st = inb(MCDPORT(1)) & 0xFF;
+#ifdef TEST1
+		printk("<int1-%02X>", st);
+#endif
+	if (!(st & MFL_STATUS))
+	{
+		st = inb(MCDPORT(0)) & 0xFF;
+#ifdef TEST1
+		printk("<int0-%02X>", st);
+#endif
+		if ((st & 0xFF) != 0xFF)
+		  mcd_error = st ? st & 0xFF : -1;
+	}
+}
+
+
+static void
+do_mcd_request(void)
+{
+#ifdef TEST2
+  printk(" do_mcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+  mcd_transfer_is_active = 1;
+  while (CURRENT_VALID) {
+    if (CURRENT->bh) {
+      if (!CURRENT->bh->b_lock)
+	panic(DEVICE_NAME ": block not locked");
+    }
+    mcd_transfer();
+    if (CURRENT -> nr_sectors == 0) {
+      end_request(1);
+    } else {
+      mcd_buf_out = -1;		/* Want to read a block not in buffer */
+      if (mcd_state == MCD_S_IDLE) {
+	if (!tocUpToDate) {
+	  if (updateToc() < 0) {
+	    while (CURRENT_VALID)
+	      end_request(0);
+	    break;
+	  }
+	}
+	mcd_state = MCD_S_START;
+	McdTries = 5;
+	SET_TIMER(mcd_poll, 1);
+      }
+      break;
+    }
+  }
+  mcd_transfer_is_active = 0;
+#ifdef TEST2
+  printk(" do_mcd_request ends\n");
+#endif
+}
+
+
+
+static void
+mcd_poll(void)
+{
+  int st;
+
+
+  if (mcd_error) 
+  {
+    if (mcd_error & 0xA5) 
+    {
+      printk("mcd: I/O error 0x%02x", mcd_error);
+      if (mcd_error & 0x80)
+	printk(" (Door open)");
+      if (mcd_error & 0x20)
+	printk(" (Disk changed)");
+      if (mcd_error & 0x04)
+	{
+	printk(" (Read error)"); /* Bitch about the problem. */
+	
+	/* Time to get fancy! If at 2x speed and 1 error, drop to 1x speed! */
+	/* Interesting how it STAYS at MCD_RETRY_ATTEMPTS on first error! */
+	/* But I find that rather HANDY!!! */
+	/* Neat! it REALLY WORKS on those LOW QUALITY CD's!!! Smile! :) */
+	/* AJK [06/17/95] */
+	
+	/* Slap the CD down to single speed! */
+	if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_2X_READ) 
+		{
+		MCMD_DATA_READ = MCMD_PLAY_READ; /* Uhhh, Ummmm, muhuh-huh! */
+		mcd1xhold = SINGLE_HOLD_SECTORS; /* Hey Bevis! */
+		printk(" Speed now 1x");	 /* Pull my finger! */
+		}
+	}
+      printk("\n");
+      mcd_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+      if (McdTries == MCD_RETRY_ATTEMPTS)
+	printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+      if (!McdTries--) 
+        {
+	/* Nuts! This cd is ready for recycling! */
+	/* When WAS the last time YOU cleaned it CORRECTLY?! */
+	printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+	if (mcd_transfer_is_active) 
+	{
+	  McdTries = 0;
+	  goto ret;
+	}
+	if (CURRENT_VALID)
+	  end_request(0);
+	McdTries = MCD_RETRY_ATTEMPTS;
+      }
+    }
+    mcd_error = 0;
+    mcd_state = MCD_S_STOP;
+  }
+	/* Switch back to Double speed if enough GOOD sectors were read! */
+	
+	/* Are we a double speed with a crappy CD?! */
+    if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_PLAY_READ)
+    	{
+	/* We ARE a double speed and we ARE bitching! */
+	if (mcd1xhold == 0) /* Okay, Like are we STILL at single speed? */
+		{ /* We need to switch back to double speed now... */
+		MCMD_DATA_READ = MCMD_2X_READ; /* Uhhh... BACK You GO! */
+		printk("mcd: Switching back to 2X speed!\n"); /* Tell 'em! */
+		}
+	else mcd1xhold--; /* No?! Count down the good reads some more... */
+				/* and try, try again! */
+    	}
+
+
+
+ immediately:
+  switch (mcd_state) {
+
+
+
+  case MCD_S_IDLE:
+#ifdef TEST3
+    printk("MCD_S_IDLE\n");
+#endif
+    return;
+
+
+
+  case MCD_S_START:
+#ifdef TEST3
+    printk("MCD_S_START\n");
+#endif
+
+    outb(MCMD_GET_STATUS, MCDPORT(0));
+    mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE;
+    McdTimeout = 3000;
+    break;
+
+
+
+  case MCD_S_MODE:
+#ifdef TEST3
+    printk("MCD_S_MODE\n");
+#endif
+
+    if ((st = mcdStatus()) != -1) {
+
+      if (st & MST_DSK_CHG) {
+	mcdDiskChanged = 1;
+	tocUpToDate = 0;
+	mcd_invalidate_buffers();
+      }
+
+    set_mode_immediately:
+
+      if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+	mcdDiskChanged = 1;
+	tocUpToDate = 0;
+	if (mcd_transfer_is_active) {
+	  mcd_state = MCD_S_START;
+	  goto immediately;
+	}
+	printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+	mcd_state = MCD_S_IDLE;
+	while (CURRENT_VALID)
+	  end_request(0);
+	return;
+      }
+
+      outb(MCMD_SET_MODE, MCDPORT(0));
+      outb(1, MCDPORT(0));
+      mcd_mode = 1;
+      mcd_state = MCD_S_READ;
+      McdTimeout = 3000;
+
+    }
+    break;
+
+
+
+  case MCD_S_READ:
+#ifdef TEST3
+    printk("MCD_S_READ\n");
+#endif
+
+    if ((st = mcdStatus()) != -1) {
+
+      if (st & MST_DSK_CHG) {
+	mcdDiskChanged = 1;
+	tocUpToDate = 0;
+	mcd_invalidate_buffers();
+      }
+
+    read_immediately:
+
+      if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+	mcdDiskChanged = 1;
+	tocUpToDate = 0;
+	if (mcd_transfer_is_active) {
+	  mcd_state = MCD_S_START;
+	  goto immediately;
+	}
+	printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+	mcd_state = MCD_S_IDLE;
+	while (CURRENT_VALID)
+	  end_request(0);
+	return;
+      }
+
+      if (CURRENT_VALID) {
+	struct mcd_Play_msf msf;
+	mcd_next_bn = CURRENT -> sector / 4;
+	hsg2msf(mcd_next_bn, &msf.start);
+	msf.end.min = ~0;
+	msf.end.sec = ~0;
+	msf.end.frame = ~0;
+	sendMcdCmd(MCMD_DATA_READ, &msf);
+	mcd_state = MCD_S_DATA;
+	McdTimeout = READ_TIMEOUT;
+      } else {
+	mcd_state = MCD_S_STOP;
+	goto immediately;
+      }
+
+    }
+    break;
+
+
+  case MCD_S_DATA:
+#ifdef TEST3
+    printk("MCD_S_DATA\n");
+#endif
+
+    st = inb(MCDPORT(1)) & (MFL_STATUSorDATA);
+  data_immediately:
+#ifdef TEST5
+    printk("Status %02x\n",st);
+#endif
+    switch (st) {
+
+    case MFL_DATA:
+#ifdef WARN_IF_READ_FAILURE
+      if (McdTries == 5)
+	printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+      if (!McdTries--) {
+	printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+	if (mcd_transfer_is_active) {
+	  McdTries = 0;
+	  break;
+	}
+	if (CURRENT_VALID)
+	  end_request(0);
+	McdTries = 5;
+      }
+      mcd_state = MCD_S_START;
+      McdTimeout = READ_TIMEOUT;
+      goto immediately;
+
+    case MFL_STATUSorDATA:
+      break;
+
+    default:
+      McdTries = 5;
+      if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) {
+	mcd_state = MCD_S_STOP;
+	goto immediately;
+      }
+      mcd_buf_bn[mcd_buf_in] = -1;
+      READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048);
+      mcd_buf_bn[mcd_buf_in] = mcd_next_bn++;
+      if (mcd_buf_out == -1)
+	mcd_buf_out = mcd_buf_in;
+      mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1;
+      if (!mcd_transfer_is_active) {
+	while (CURRENT_VALID) {
+	  mcd_transfer();
+	  if (CURRENT -> nr_sectors == 0)
+	    end_request(1);
+	  else
+	    break;
+	}
+      }
+
+      if (CURRENT_VALID
+	  && (CURRENT -> sector / 4 < mcd_next_bn || 
+	      CURRENT -> sector / 4 > mcd_next_bn + 16)) {
+	mcd_state = MCD_S_STOP;
+	goto immediately;
+      }
+      McdTimeout = READ_TIMEOUT;
+#ifdef DOUBLE_QUICK_ONLY
+      if (MCMD_DATA_READ != MCMD_PLAY_READ)
+#endif
+      {
+	int count= QUICK_LOOP_COUNT;
+	while (count--) {
+          QUICK_LOOP_DELAY;
+	  if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) {
+#   ifdef TEST4
+/*	    printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */
+	    printk(" %d ",QUICK_LOOP_COUNT-count);
+#   endif
+	    goto data_immediately;
+	  }
+	}
+#   ifdef TEST4
+/*      printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */
+	printk("ended ");
+#   endif
+      }
+      break;
+    }
+    break;
+
+
+
+  case MCD_S_STOP:
+#ifdef TEST3
+    printk("MCD_S_STOP\n");
+#endif
+
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+    if (!mitsumi_bug_93_wait)
+      goto do_not_work_around_mitsumi_bug_93_1;
+
+    McdTimeout = mitsumi_bug_93_wait;
+    mcd_state = 9+3+1;
+    break;
+
+  case 9+3+1:
+    if (McdTimeout)
+      break;
+
+  do_not_work_around_mitsumi_bug_93_1:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+    outb(MCMD_STOP, MCDPORT(0));
+
+#ifdef WORK_AROUND_MITSUMI_BUG_92
+    if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+      int i = 4096;
+      do {
+	inb(MCDPORT(0));
+      } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+      outb(MCMD_STOP, MCDPORT(0));
+      if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+	i = 4096;
+	do {
+	  inb(MCDPORT(0));
+	} while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+	outb(MCMD_STOP, MCDPORT(0));
+      }
+    }
+#endif /* WORK_AROUND_MITSUMI_BUG_92 */
+
+    mcd_state = MCD_S_STOPPING;
+    McdTimeout = 1000;
+    break;
+
+  case MCD_S_STOPPING:
+#ifdef TEST3
+    printk("MCD_S_STOPPING\n");
+#endif
+
+    if ((st = mcdStatus()) == -1 && McdTimeout)
+      break;
+
+    if ((st != -1) && (st & MST_DSK_CHG)) {
+      mcdDiskChanged = 1;
+      tocUpToDate = 0;
+      mcd_invalidate_buffers();
+    }
+
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+    if (!mitsumi_bug_93_wait)
+      goto do_not_work_around_mitsumi_bug_93_2;
+
+    McdTimeout = mitsumi_bug_93_wait;
+    mcd_state = 9+3+2;
+    break;
+
+  case 9+3+2:
+    if (McdTimeout)
+      break;
+
+    st = -1;
+
+  do_not_work_around_mitsumi_bug_93_2:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+#ifdef TEST3
+    printk("CURRENT_VALID %d mcd_mode %d\n",
+	   CURRENT_VALID, mcd_mode);
+#endif
+
+    if (CURRENT_VALID) {
+      if (st != -1) {
+	if (mcd_mode == 1)
+	  goto read_immediately;
+	else
+	  goto set_mode_immediately;
+      } else {
+	mcd_state = MCD_S_START;
+	McdTimeout = 1;
+      }
+    } else {
+      mcd_state = MCD_S_IDLE;
+      return;
+    }
+    break;
+
+  default:
+    printk("mcd: invalid state %d\n", mcd_state);
+    return;
+  }
+
+ ret:
+  if (!McdTimeout--) {
+    printk("mcd: timeout in state %d\n", mcd_state);
+    mcd_state = MCD_S_STOP;
+  }
+
+  SET_TIMER(mcd_poll, 1);
+}
+
+
+
+static void
+mcd_invalidate_buffers(void)
+{
+  int i;
+  for (i = 0; i < MCD_BUF_SIZ; ++i)
+    mcd_buf_bn[i] = -1;
+  mcd_buf_out = -1;
+}
+
+
+/*
+ * Open the device special file.  Check that a disk is in.
+ */
+
+int
+mcd_open(struct inode *ip, struct file *fp)
+{
+	int st;
+
+	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();
+
+	st = statusCmd();			/* check drive status */
+	if (st == -1)
+		return -EIO;			/* drive doesn't respond */
+
+	if ((st & MST_READY) == 0)		/* no disk in drive */
+	{
+		printk("mcd: no disk in drive\n");
+		return -EIO;
+	}
+
+	if (updateToc() < 0)
+		return -EIO;
+
+	}
+	++mcd_open_count;
+        MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+/*
+ * On close, we flush all mcd blocks from the buffer cache.
+ */
+
+static void
+mcd_release(struct inode * inode, struct file * file)
+{ MOD_DEC_USE_COUNT;
+  if (!--mcd_open_count) {
+	mcd_invalidate_buffers();
+	sync_dev(inode->i_rdev);
+	invalidate_buffers(inode -> i_rdev);
+  }
+}
+
+
+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,			/* select */
+	mcd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	mcd_open,		/* open */
+	mcd_release,		/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	check_mcd_change,	/* media change */
+	NULL			/* revalidate */
+};
+
+
+/*
+ * Test for presence of drive and initialize it.  Called at boot time.
+ */
+
+int
+mcd_init(void)
+{
+	int count;
+	unsigned char result[3];
+
+	if (mcd_port <= 0 || mcd_irq <= 0) {
+	  printk("skip mcd_init\n");
+          return -EIO;
+	}
+
+	printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
+
+	if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
+	{
+		printk("Unable to get major %d for Mitsumi CD-ROM\n",
+		       MAJOR_NR);
+                return -EIO;
+	}
+
+        if (check_region(mcd_port, 4)) {
+	  printk("Init failed, I/O port (%X) already in use\n",
+		 mcd_port);
+          return -EIO;
+	}
+	  
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+
+	/* check for card */
+
+	outb(0, MCDPORT(1));			/* send reset */
+	for (count = 0; count < 2000000; count++)
+		(void) inb(MCDPORT(1));		/* delay a bit */
+
+	outb(0x40, MCDPORT(0));			/* send get-stat cmd */
+	for (count = 0; count < 2000000; count++)
+		if (!(inb(MCDPORT(1)) & MFL_STATUS))
+			break;
+
+	if (count >= 2000000) {
+		printk("Init failed. No mcd device at 0x%x irq %d\n",
+		     mcd_port, mcd_irq);
+                return -EIO;
+	}
+	count = inb(MCDPORT(0));		/* pick up the status */
+	
+	outb(MCMD_GET_VERSION,MCDPORT(0));
+	for(count=0;count<3;count++)
+		if(getValue(result+count)) {
+			printk("mitsumi get version failed at 0x%d\n",
+			       mcd_port);
+                        return -EIO;
+		}	
+
+	if (result[0] == result[1] && result[1] == result[2])
+                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];
+
+	if (mcdVersion >=4)
+		outb(4,MCDPORT(2)); 	/* magic happens */
+
+	/* don't get the IRQ until we know for sure the drive is there */
+
+	if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD"))
+	{
+		printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
+                return -EIO;
+	}
+	request_region(mcd_port, 4,"mcd");
+
+	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
+	outb(0x02,MCDPORT(0));
+	outb(0x00,MCDPORT(0));
+	getValue(result);
+
+	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
+	outb(0x10,MCDPORT(0));
+	outb(0x04,MCDPORT(0));
+	getValue(result);
+
+	mcd_invalidate_buffers();
+	mcdPresent = 1;
+        return 0;
+}
+
+
+static void
+hsg2msf(long hsg, struct msf *msf)
+{
+	hsg += 150;
+	msf -> min = hsg / 4500;
+	hsg %= 4500;
+	msf -> sec = hsg / 75;
+	msf -> frame = hsg % 75;
+
+	bin2bcd(&msf -> min);		/* convert to BCD */
+	bin2bcd(&msf -> sec);
+	bin2bcd(&msf -> frame);
+}
+
+
+static void
+bin2bcd(unsigned char *p)
+{
+	int u, t;
+
+	u = *p % 10;
+	t = *p / 10;
+	*p = u | (t << 4);
+}
+
+static int
+bcd2bin(unsigned char bcd)
+{
+	return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+
+
+/*
+ * See if a status is ready from the drive and return it
+ * if it is ready.
+ */
+
+static int
+mcdStatus(void)
+{
+	int i;
+	int st;
+
+	st = inb(MCDPORT(1)) & MFL_STATUS;
+	if (!st)
+	{
+		i = inb(MCDPORT(0)) & 0xFF;
+		return i;
+	}
+	else
+		return -1;
+}
+
+
+/*
+ * Send a play or read command to the drive
+ */
+
+static void
+sendMcdCmd(int cmd, struct mcd_Play_msf *params)
+{
+	outb(cmd, MCDPORT(0));
+	outb(params -> start.min, MCDPORT(0));
+	outb(params -> start.sec, MCDPORT(0));
+	outb(params -> start.frame, MCDPORT(0));
+	outb(params -> end.min, MCDPORT(0));
+	outb(params -> end.sec, MCDPORT(0));
+	outb(params -> end.frame, MCDPORT(0));
+}
+
+
+/*
+ * Timer interrupt routine to test for status ready from the drive.
+ * (see the next routine)
+ */
+
+static void
+mcdStatTimer(void)
+{
+	if (!(inb(MCDPORT(1)) & MFL_STATUS))
+	{
+		wake_up(&mcd_waitq);
+		return;
+	}
+
+	McdTimeout--;
+	if (McdTimeout <= 0)
+	{
+		wake_up(&mcd_waitq);
+		return;
+	}
+
+	SET_TIMER(mcdStatTimer, 1);
+}
+
+
+/*
+ * Wait for a status to be returned from the drive.  The actual test
+ * (see routine above) is done by the timer interrupt to avoid
+ * excessive rescheduling.
+ */
+
+static int
+getMcdStatus(int timeout)
+{
+	int st;
+
+	McdTimeout = timeout;
+	SET_TIMER(mcdStatTimer, 1);
+	sleep_on(&mcd_waitq);
+	if (McdTimeout <= 0)
+		return -1;
+
+	st = inb(MCDPORT(0)) & 0xFF;
+	if (st == 0xFF)
+		return -1;
+
+	if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY)
+		/* XXX might be an error? look at q-channel? */
+		audioStatus = CDROM_AUDIO_COMPLETED;
+
+	if (st & MST_DSK_CHG)
+	{
+		mcdDiskChanged = 1;
+		tocUpToDate = 0;
+		audioStatus = CDROM_AUDIO_NO_STATUS;
+	}
+
+	return st;
+}
+
+
+/*
+ * Read a value from the drive.  Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling.
+ */
+
+static int
+getValue(unsigned char *result)
+{
+	int count;
+	int s;
+
+	for (count = 0; count < 2000; count++)
+		if (!(inb(MCDPORT(1)) & MFL_STATUS))
+			break;
+
+	if (count >= 2000)
+	{
+		printk("mcd: getValue timeout\n");
+		return -1;
+	}
+
+	s = inb(MCDPORT(0)) & 0xFF;
+	*result = (unsigned char) s;
+	return 0;
+}
+
+
+/*
+ * Read the current Q-channel info.  Also used for reading the
+ * table of contents.
+ */
+
+int
+GetQChannelInfo(struct mcd_Toc *qp)
+{
+	unsigned char notUsed;
+	int retry;
+
+	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
+	{
+		outb(MCMD_GET_Q_CHANNEL, MCDPORT(0));
+		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
+			break;
+	}
+
+	if (retry >= MCD_RETRY_ATTEMPTS)
+		return -1;
+
+	if (getValue(&qp -> ctrl_addr) < 0) return -1;
+	if (getValue(&qp -> track) < 0) return -1;
+	if (getValue(&qp -> pointIndex) < 0) return -1;
+	if (getValue(&qp -> trackTime.min) < 0) return -1;
+	if (getValue(&qp -> trackTime.sec) < 0) return -1;
+	if (getValue(&qp -> trackTime.frame) < 0) return -1;
+	if (getValue(&notUsed) < 0) return -1;
+	if (getValue(&qp -> diskTime.min) < 0) return -1;
+	if (getValue(&qp -> diskTime.sec) < 0) return -1;
+	if (getValue(&qp -> diskTime.frame) < 0) return -1;
+
+	return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC) and TOC header if necessary
+ */
+
+static int
+updateToc()
+{
+	if (tocUpToDate)
+		return 0;
+
+	if (GetDiskInfo() < 0)
+		return -EIO;
+
+	if (GetToc() < 0)
+		return -EIO;
+
+	tocUpToDate = 1;
+	return 0;
+}
+
+
+/*
+ * Read the table of contents header
+ */
+
+static int
+GetDiskInfo()
+{
+	int retry;
+
+	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
+	{
+		outb(MCMD_GET_DISK_INFO, MCDPORT(0));
+		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
+			break;
+	}
+
+	if (retry >= MCD_RETRY_ATTEMPTS)
+		return -1;
+
+	if (getValue(&DiskInfo.first) < 0) return -1;
+	if (getValue(&DiskInfo.last) < 0) return -1;
+
+	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,
+	DiskInfo.last,
+	DiskInfo.diskLength.min,
+	DiskInfo.diskLength.sec,
+	DiskInfo.diskLength.frame,
+	DiskInfo.firstTrack.min,
+	DiskInfo.firstTrack.sec,
+	DiskInfo.firstTrack.frame);
+#endif
+
+	return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC)
+ */
+
+static int
+GetToc()
+{
+	int i, px;
+	int limit;
+	int retry;
+	struct mcd_Toc qInfo;
+
+	for (i = 0; i < MAX_TRACKS; i++)
+		Toc[i].pointIndex = 0;
+
+	i = DiskInfo.last + 3;
+
+	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
+	{
+		outb(MCMD_STOP, MCDPORT(0));
+		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
+			break;
+	}
+
+	if (retry >= MCD_RETRY_ATTEMPTS)
+		return -1;
+
+	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
+	{
+		outb(MCMD_SET_MODE, MCDPORT(0));
+		outb(0x05, MCDPORT(0));			/* mode: toc */
+		mcd_mode = 0x05;
+		if (getMcdStatus(MCD_STATUS_DELAY) != -1)
+			break;
+	}
+
+	if (retry >= MCD_RETRY_ATTEMPTS)
+		return -1;
+
+	for (limit = 300; limit > 0; limit--)
+	{
+		if (GetQChannelInfo(&qInfo) < 0)
+			break;
+
+		px = bcd2bin(qInfo.pointIndex);
+		if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+			if (Toc[px].pointIndex == 0)
+			{
+				Toc[px] = qInfo;
+				i--;
+			}
+
+		if (i <= 0)
+			break;
+	}
+
+	Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+
+	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
+	{
+                outb(MCMD_SET_MODE, MCDPORT(0));
+                outb(0x01, MCDPORT(0));
+		mcd_mode = 1;
+                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
+                        break;
+	}
+
+#ifdef MCD_DEBUG
+for (i = 1; i <= DiskInfo.last; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+for (i = 100; i < 103; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+#endif
+
+	return limit > 0 ? 0 : -1;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{ if (MOD_IN_USE)
+     { printk("mcd module in use - can't remove it.\n");
+       return;    
+     }
+  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);
+  printk("mcd module released.\n");
+}
+#endif MODULE
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- v1.3.35/linux/drivers/cdrom/mcdx.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/mcdx.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,1727 @@
+/*
+ * The Mitsumi CDROM interface
+ * Copyright (C) 1995 Heiko Schlittermann
+ * VERSION: 1.0a
+ * 
+ * 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 Harriss (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 (extensivly tested the LU portion)
+ *  ... somebody forgotten?
+ *  
+ */
+
+
+#if RCS
+static const char *mcdx_c_version
+		= "mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp";
+#endif
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#define MOD_IN_USE 1
+#endif MODULE
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+
+#include <linux/major.h>
+
+#ifndef MITSUMI_X_CDROM_MAJOR               /* old kernel (doesn't know about MCDX) */
+#define MITSUMI_X_CDROM_MAJOR 20
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcdx */
+#define DEVICE_REQUEST do_mcdx_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#endif
+
+#define MAJOR_NR MITSUMI_X_CDROM_MAJOR
+#include <linux/blk.h>
+#define	mcdx_drive_map mcdx     /* for compatible parameter passing with "insmod" */
+#include <linux/mcdx.h>
+
+/* CONSTANTS *******************************************************/
+
+const int REQUEST_SIZE = 200;
+const int DIRECT_SIZE = 200;
+
+enum drivemodes { TOC, DATA, RAW, COOKED };
+enum datamodes { MODE0, MODE1, MODE2 };
+enum resetmodes { SOFT, HARD };
+
+const int SINGLE = 0x01;
+const int DOUBLE = 0x02;
+const int DOOR   = 0x04;
+const int MULTI  = 0x08;
+const int READY  = 0x70;
+
+const unsigned char READSSPEED = 0xc0;
+const unsigned char READDSPEED = 0xc1;
+
+
+/* DECLARATIONS ****************************************************/ 
+struct s_msf {
+	unsigned char minute;
+	unsigned char second;
+	unsigned char frame;
+};
+
+struct s_subqcode {
+	unsigned char control;
+	unsigned char tno;
+	unsigned char index;
+	struct s_msf tt;
+	struct s_msf dt;
+};
+
+struct s_diskinfo {
+	unsigned int n_first;
+	unsigned int n_last;
+	struct s_msf msf_leadout;
+	struct s_msf msf_first;
+};
+	
+struct s_multi {
+	unsigned char multi;
+	struct s_msf msf_last;
+};
+
+struct s_version {
+	unsigned char code;
+	unsigned char ver;
+};
+
+/* Per drive/controller stuff **************************************/
+
+struct s_drive_stuff {
+	/* waitquenes */
+    struct wait_queue *busyq;
+    struct wait_queue *lockq;
+    struct wait_queue *sleepq;
+
+ 	/* flags */
+    volatile int introk;	/* status of last irq operation */
+    volatile int busy;		/* drive performs an operation */
+    volatile int lock;		/* exclusive usage */
+
+	/* cd infos */
+	struct s_diskinfo di;
+	struct s_multi multi;
+	struct s_subqcode* toc;	/* first enty of the toc array */
+	struct s_subqcode start;
+    struct s_subqcode stop;
+	int xa;					/* 1 if xa disk */
+	int audio;				/* 1 if audio disk */
+	int audiostatus;			
+
+	/* `buffer' control */
+    volatile int valid;
+    volatile int pending;
+    volatile int off_direct;
+    volatile int off_requested;
+
+	/* adds and odds */
+	void* wreg_data;	/* w data */
+	void* wreg_reset;	/* w hardware reset */
+	void* wreg_hcon;	/* w hardware conf */
+	void* wreg_chn;		/* w channel */
+	void* rreg_data;	/* r data */
+	void* rreg_status;	/* r status */
+
+    int irq;			/* irq used by this drive */
+    int minor;			/* minor number of this drive */
+    int present;	    /* drive present and its capabilities */
+    char readcmd;		/* read cmd depends on single/double speed */
+    char playcmd;       /* play should always be single speed */
+    unsigned long changed;	/* last jiff the media was changed */
+    unsigned long xxx;      /* last jiff it was asked for media change */
+    int users;				/* keeps track of open/close */
+    int lastsector;			/* last block accessible */
+    int errno;				/* last operation's error */
+
+};
+
+
+/* Prototypes ******************************************************/ 
+
+/*	The following prototypes are already declared elsewhere.  They are
+ 	repeated here to show what's going on.  And to sense, if they're
+	changed elsewhere. */
+
+/* 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. */
+
+/* ???  exported by the mcdx_sigaction struct */
+static void mcdx_intr(int, struct pt_regs*);
+
+/* exported by file_ops */
+static int mcdx_open(struct inode*, struct file*);
+static void mcdx_close(struct inode*, struct file*);
+static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
+
+/* misc internal support functions */
+static void log2msf(unsigned int, struct s_msf*);
+static unsigned int msf2log(const struct s_msf*);
+static unsigned int uint2bcd(unsigned int);
+static unsigned int bcd2uint(unsigned char);
+#if MCDX_DEBUG
+static void TRACE((int level, const char* fmt, ...));
+#endif
+static void warn(const char* fmt, ...);
+static char *port(int*);
+static int irq(int*);
+static void mcdx_delay(struct s_drive_stuff*, long jifs);
+static int mcdx_transfer(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);
+static int mcdx_requestmultidiskinfo(struct s_drive_stuff*, struct s_multi*, int);
+static int mcdx_requesttocdata(struct s_drive_stuff*, struct s_diskinfo*, int);
+static int mcdx_getstatus(struct s_drive_stuff*, int);
+static int mcdx_getval(struct s_drive_stuff*, int to, int delay, char*);
+
+static int mcdx_talk(struct s_drive_stuff*, 
+		const unsigned char* cmd, size_t, void *buffer,
+		size_t size, unsigned int timeout, int);
+static int mcdx_readtoc(struct s_drive_stuff*);
+static int mcdx_playtrk(struct s_drive_stuff*, const struct cdrom_ti*);
+static int mcdx_playmsf(struct s_drive_stuff*, const struct cdrom_msf*);
+
+/* static variables ************************************************/
+
+static int dummy0;
+static int mcdx_drive_map[][2] = MCDX_DRIVEMAP;
+static struct s_drive_stuff* mcdx_stuffp[MCDX_NDRIVES];
+static struct s_drive_stuff* mcdx_irq_map[16] =
+		{0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0};
+
+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 select */
+	mcdx_ioctl,		/* ioctl() */
+	NULL,			/* no mmap */
+	mcdx_open,		/* open() */
+	mcdx_close,		/* close() */
+	NULL,			/* fsync */
+	NULL,                   /* fasync */
+	check_mcdx_media_change, /* media_change */
+	NULL                    /* revalidate */
+};
+
+/* KERNEL INTERFACE FUNCTIONS **************************************/ 
+
+static int 
+mcdx_ioctl(
+	struct inode* ip, struct file* fp, 
+	unsigned int cmd, unsigned long arg)
+{ 
+	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+
+	if (!stuffp->present) return -ENXIO;
+	if (!ip) return -EINVAL;
+
+	switch (cmd) {
+		case CDROMSTART: {
+			TRACE((IOCTL, "ioctl() START\n"));
+			return 0;
+		}
+
+		case CDROMSTOP: {
+			TRACE((IOCTL, "ioctl() STOP\n"));
+            stuffp->audiostatus = CDROM_AUDIO_INVALID;
+			if (-1 == mcdx_stop(stuffp, 1))
+				return -EIO;
+			return 0;
+		}
+
+		case CDROMPLAYTRKIND: {
+			int ans;
+			struct cdrom_ti ti;
+
+			TRACE((IOCTL, "ioctl() PLAYTRKIND\n"));
+			if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti))))
+				return ans;
+			memcpy_fromfs(&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;
+            TRACE((IOCTL, "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;
+
+            TRACE((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;
+
+            memcpy_fromfs(&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_min1 = uint2bcd(msf.cdmsf_min1);
+            msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1);
+            msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1);
+
+            return mcdx_playmsf(stuffp, &msf);
+        }
+
+        case CDROMRESUME: {
+            TRACE((IOCTL, "ioctl() RESUME\n"));
+            return mcdx_playtrk(stuffp, NULL);
+        }
+
+		case CDROMREADTOCENTRY: {
+			struct cdrom_tocentry entry;
+			struct s_subqcode *tp = NULL;
+			int ans;
+
+			TRACE((IOCTL, "ioctl() READTOCENTRY\n"));
+
+            if (-1 == mcdx_readtoc(stuffp)) return -1;
+
+			if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(entry)))) return ans;
+			memcpy_fromfs(&entry, (void *) arg, sizeof(entry));
+
+			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) WARN(("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;
+
+			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(entry)))) return ans;
+			memcpy_tofs((void*) arg, &entry, sizeof(entry));
+
+			return 0;
+		}
+
+		case CDROMSUBCHNL: {
+			int ans;
+			struct cdrom_subchnl sub;
+			struct s_subqcode q;
+
+			TRACE((IOCTL, "ioctl() SUBCHNL\n"));
+
+			if ((ans = verify_area(VERIFY_READ, 
+                    (void*) arg, sizeof(sub)))) return ans;
+
+			memcpy_fromfs(&sub, (void*) arg, sizeof(sub));
+
+			if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO;
+
+            TRACE((IOCTL, "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);
+
+            TRACE((IOCTL, "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);
+                TRACE((IOCTL, "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);
+                TRACE((IOCTL,
+                        "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;
+
+			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(sub))))
+				return ans;
+			memcpy_tofs((void*) arg, &sub, sizeof(sub));
+
+			return 0;
+		}
+
+		case CDROMREADTOCHDR: {
+			struct cdrom_tochdr toc;
+			int ans;
+
+			TRACE((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;
+			memcpy_tofs((void*) arg, &toc, sizeof toc);
+			TRACE((IOCTL, "ioctl() track0 = %d, track1 = %d\n",
+					stuffp->di.n_first, stuffp->di.n_last));
+			return 0;
+		}
+
+		case CDROMPAUSE: {
+			TRACE((IOCTL, "ioctl() PAUSE\n"));
+			if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL;
+			if (-1 == mcdx_stop(stuffp, 1)) return -EIO;
+			if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1))
+				return -EIO;
+            
+            stuffp->audiostatus = CDROM_AUDIO_PAUSED;
+			return 0;
+		}
+
+		case CDROMMULTISESSION: {
+			int ans;
+			struct cdrom_multisession ms;
+			TRACE((IOCTL, "ioctl() MULTISESSION\n"));
+			if (0 != (ans = verify_area(VERIFY_READ, (void*) arg, 
+					sizeof(struct cdrom_multisession))))
+				return ans;
+				
+			memcpy_fromfs(&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->xa;
+
+			if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg,
+					sizeof(struct cdrom_multisession))))
+				return ans;
+
+			memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
+			if (ms.addr_format == CDROM_MSF) 
+				TRACE((IOCTL, 
+						"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
+			  {
+			    dummy0=0;
+			    TRACE((IOCTL, 
+					"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: {
+			TRACE((IOCTL, "ioctl() EJECT\n"));
+			if (stuffp->users > 1) return -EBUSY;
+			if (-1 == mcdx_eject(stuffp, 1)) return -EIO;
+			return 0;
+		}
+
+        case CDROMVOLCTRL: {
+            TRACE((IOCTL, "ioctl() volctrl\n"));
+            return 0;
+        }
+
+		default:
+			WARN(("ioctl(): unknown request 0x%04x\n", cmd));
+	    	return -EINVAL;
+	}
+}
+
+void do_mcdx_request()
+{
+    int dev;
+    struct s_drive_stuff *stuffp;
+
+  again:
+
+	TRACE((REQUEST, "do_request()\n"));
+
+	if ((CURRENT == NULL) || (CURRENT->rq_status == RQ_INACTIVE))  {
+		TRACE((REQUEST, "do_request() done\n"));
+		return;
+	}
+
+    stuffp = mcdx_stuffp[MINOR(CURRENT->rq_dev)];
+	TRACE((REQUEST, "do_request() stuffp = %p\n", stuffp));
+
+    INIT_REQUEST;
+    dev = MINOR(CURRENT->rq_dev);
+
+	if ((dev < 0) || (dev >= MCDX_NDRIVES) || (!stuffp->present)) {
+		WARN(("do_request(): bad device: %s\n",
+		      kdevname(CURRENT->rq_dev)));
+		end_request(0);
+		goto again;
+    }
+
+	if (stuffp->audio) {
+		WARN(("do_request() attempt to read from audio cd\n"));
+		end_request(0);
+		goto again;
+	}
+
+    switch (CURRENT->cmd) {
+      case WRITE:
+	  WARN(("do_request(): attempt to write to cd!!\n"));
+	  end_request(0);
+	  break;
+
+      case READ:
+	  stuffp->errno = 0;
+	  while (CURRENT->nr_sectors) {
+	      int i;
+
+	      if (-1 == (i = mcdx_transfer(
+				      stuffp,
+				      CURRENT->buffer,
+				      CURRENT->sector,
+				      CURRENT->nr_sectors))) {
+          WARN(("do_request() read error\n"));
+		  if (stuffp->errno == MCDX_EOM) {
+		      CURRENT->sector += CURRENT->nr_sectors;
+		      CURRENT->nr_sectors = 0;
+		  }
+		  end_request(0);
+		  goto again;
+	      }
+	      CURRENT->sector += i;
+	      CURRENT->nr_sectors -= i;
+	      CURRENT->buffer += (i * 512);
+
+	  }
+
+	  end_request(1);
+	  break;
+
+      default:
+	  panic(MCDX "do_request: unknown command.\n");
+	  break;
+    }
+
+    goto again;
+}
+
+static int 
+mcdx_open(struct inode *ip, struct file *fp)
+{
+    struct s_drive_stuff *stuffp;
+
+	TRACE((OPENCLOSE, "open()\n"));
+    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+    if (!stuffp->present) return -ENXIO;
+
+    if (-1 == mcdx_getstatus(stuffp, 1)) return -EIO;
+
+	/* close the door, if necessary (get the door information
+    from the hardware status register) */
+	if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR)  
+		mcdx_closedoor(stuffp, 1);
+
+	/* if the media changed we will have to little more */
+	if (stuffp->xxx < stuffp->changed) {
+
+		TRACE((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. */
+
+        /*
+        TRACE((OPENCLOSE, "open() hardware reset\n"));
+		if (-1 == mcdx_reset(stuffp, HARD, 1)) return -EIO;
+        */
+
+        stuffp->audiostatus = CDROM_AUDIO_INVALID;
+
+		/* get the multisession information */
+		{
+			TRACE((OPENCLOSE, "open() Request multisession info\n"));
+			if (-1 == mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6))
+				return -EIO;
+		
+			if (stuffp->multi.multi > 2)
+				WARN(("open() unknown multisession value (%d)\n", stuffp->multi.multi));
+
+			/* multisession ? */
+			if (!stuffp->multi.multi)
+				stuffp->multi.msf_last.second = 2;
+
+			TRACE((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)) return -EIO;
+
+		stuffp->lastsector = (CD_FRAMESIZE / 512) 
+                * msf2log(&stuffp->di.msf_leadout) - 1;
+
+		TRACE((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)));
+		TRACE((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) {
+			TRACE((MALLOC, "open() free toc @ %p\n", stuffp->toc));
+			kfree(stuffp->toc);
+		}
+		stuffp->toc = NULL;
+
+		TRACE((OPENCLOSE, "open() init irq generation\n"));
+		if (-1 == mcdx_config(stuffp, 1)) return -EIO;
+
+		/* try to get the first sector ... */
+		{
+			char buf[512];
+			int ans;
+			int tries;
+
+			stuffp->xa = 0;
+			stuffp->audio = 0;
+
+			for (tries = 6; tries; tries--) {
+				TRACE((OPENCLOSE, "open() try as %s\n",
+					stuffp->xa ? "XA" : "normal"));
+
+				/* set data mode */
+				if (-1 == (ans = mcdx_setdatamode(stuffp, 
+						stuffp->xa ? MODE2 : MODE1, 1)))
+					return -EIO;
+
+				if ((stuffp->audio = e_audio(ans))) break; 
+
+				while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) 
+					;
+
+				if (ans == 1) break;
+				stuffp->xa = !stuffp->xa; 
+			}
+			if (!tries) return -EIO;
+		}
+
+		/* xa disks will be read in raw mode, others not */
+		if (-1 == mcdx_setdrivemode(stuffp, 
+				stuffp->xa ? RAW : COOKED, 1))
+			return -EIO;
+
+		if (stuffp->audio) {
+			INFO(("open() audio disk found\n"));
+		} else {
+			INFO(("open() %s%s disk found\n",
+					stuffp->xa ? "XA / " : "",
+					stuffp->multi.multi ? "Multi Session" : "Single Session"));
+		}
+
+        stuffp->xxx = jiffies;
+	}
+
+    /* lock the door if not already done */
+    if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1))) 
+        return -EIO;
+
+    stuffp->users++;
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static void 
+mcdx_close(struct inode *ip, struct file *fp)
+{
+    struct s_drive_stuff *stuffp;
+
+    TRACE((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 (-1 == mcdx_lockdoor(stuffp, 0, 1))
+				printk(MCDX ": Cannot unlock the door\n");
+    }
+    MOD_DEC_USE_COUNT;
+
+    return;
+}
+
+int check_mcdx_media_change(kdev_t full_dev)
+/*	Return: 1 if media changed since last call to 
+			  this function
+			0 else
+	Setting flag to 0 resets the changed state. */
+
+{
+    INFO(("check_mcdx_media_change called for device %s\n",
+	  kdevname(full_dev)));
+    return 0;
+}
+
+void mcdx_setup(char *str, int *pi)
+{
+#if MCDX_DEBUG
+    printk(MCDX ":: setup(%s, %d) called\n",
+	    str, pi[0]);
+#endif
+}
+
+/* DIRTY PART ******************************************************/ 
+
+static void mcdx_delay(struct s_drive_stuff *stuff, long jifs)
+/*	This routine is used for sleeping while initialisation - it seems that
+ 	there are no other means available. May be we could use a simple count
+ 	loop w/ jumps to itself, but I wanna make this independend of cpu
+ 	speed. [1 jiffie is 1/HZ sec */
+{
+    unsigned long tout = jiffies + jifs;
+
+    TRACE((INIT, "mcdx_delay %d\n", jifs));
+    if (jifs < 0) return;
+
+#if 1
+    while (jiffies < tout) {
+        current->timeout = jiffies;
+        schedule();
+    }
+#else
+    if (current->pid == 0) {        /* no sleep allowed */
+		while (jiffies < tout) {
+            current->timeout = jiffies;
+            schedule();
+        }
+    } else {                        /* sleeping is allowed */
+        current->timeout = tout;
+        current->state = TASK_INTERRUPTIBLE;
+        while (current->timeout) {
+            interruptible_sleep_on(&stuff->sleepq);
+        }
+    }
+#endif
+}
+
+static void 
+mcdx_intr(int irq, struct pt_regs* regs)
+{
+    struct s_drive_stuff *stuffp;
+	unsigned char x;
+
+    stuffp = mcdx_irq_map[irq];
+
+    if (!stuffp->busy) {
+		INFO(("intr() unexpected interrupt @ irq %d\n", irq));
+		return;
+    }
+
+	/* if not ok read the next byte as the drives status */
+	if (0 == (stuffp->introk = 
+			(~(x = inb((unsigned int) stuffp->rreg_status)) & MCDX_RBIT_DTEN))) 
+		TRACE((IRQ, "intr() irq %d failed, status %02x %02x\n",
+				irq, x, inb((unsigned int) stuffp->rreg_data)));
+	else
+	  {
+	    dummy0=0;
+	    TRACE((IRQ, "irq() irq %d ok, status %02x\n", irq, x));
+	  }
+    stuffp->busy = 0;
+    wake_up_interruptible(&stuffp->busyq);
+}
+
+
+static int 
+mcdx_talk (
+		struct s_drive_stuff *stuffp, 
+		const unsigned char *cmd, size_t cmdlen,
+		void *buffer, size_t size, 
+		unsigned int timeout, int tries)
+/* Send a command to the drive, wait for the result.
+ * returns -1 on timeout, drive status otherwise
+ */
+{
+	char c;
+	int st;
+
+	if (!buffer || size == 0) buffer = &c, size = 1;
+
+    while (stuffp->lock)
+		interruptible_sleep_on(&stuffp->lockq);
+
+    if (current->signal && ~current->blocked) {
+        WARN(("talk() got signal %d\n", current->signal));
+        return -1;
+    }
+
+    stuffp->lock = 1;
+    stuffp->valid = 0;	
+
+#if MCDX_DEBUG & TALK
+	{ 
+		unsigned char i;
+		TRACE((TALK, "talk() %d / %d tries, res.size %d, command 0x%02x", 
+				tries, timeout, size, (unsigned char) cmd[0]));
+		for (i = 1; i < cmdlen; i++) printk(" 0x%02x", cmd[i]);
+		printk("\n");
+	}
+#endif
+
+    /*  give up if all tries are done (bad) or if the status
+     *  st != -1 (good) */
+	for (st = -1; st == -1 && tries; tries--) {
+
+        size_t sz = size;
+        char* bp = buffer;
+
+		outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen);
+        TRACE((TALK, "talk() command sent\n"));
+
+        /* get the status byte */
+        if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
+            WARN(("talk() %02x timed out (status), %d tr%s left\n", 
+                    cmd[0], tries - 1, tries == 2 ? "y" : "ies"));
+                continue; 
+        }
+        st = *bp++;
+        sz--;
+
+        TRACE((TALK, "talk() got status 0x%02x\n", st));
+
+        /* command error? */
+        if (e_cmderr(st)) {
+            WARN(("command error %02x (%d)\n", cmd[0], cmdlen));
+            st = -1;
+            continue;
+        }
+
+        /* audio status? */
+        if (stuffp->audiostatus == CDROM_AUDIO_INVALID)
+            stuffp->audiostatus = 
+                    e_audiobusy(st) ? CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS;
+
+        /* media change? */
+        if (e_changed(st)) {
+            INFO(("talk() media changed\n"));
+            stuffp->changed = jiffies;
+        }
+
+        /* now actually get the data */
+        while (sz--) {
+            if (-1 == mcdx_getval(stuffp, timeout, -1, bp++)) {
+                WARN(("talk() %02x timed out (data), %d tr%s left\n", 
+                        cmd[0], tries - 1, tries == 2 ? "y" : "ies"));
+                st = -1; break;
+            }
+            TRACE((TALK, "talk() got 0x%02x\n", *(bp - 1)));
+        }
+    }
+
+#if QUIET == 0
+    if (!tries && st == -1) INFO(("talk() giving up\n"));
+#endif
+
+    stuffp->lock = 0;
+    wake_up_interruptible(&stuffp->lockq);
+
+	TRACE((TALK, "talk() done with 0x%02x\n", st));
+    return st;
+}
+
+/* MODULE STUFF ***********************************************************/
+#ifdef MODULE
+
+int init_module(void)
+{
+	int i;
+	int drives = 0;
+
+	mcdx_init();
+	for (i = 0; i < MCDX_NDRIVES; i++)  {
+		if (mcdx_stuffp[i]) {
+		TRACE((INIT, "init_module() drive %d stuff @ %p\n",
+				i, mcdx_stuffp[i]));
+			drives++;
+		}
+	}
+
+    if (!drives) 
+		return -EIO;
+
+    return 0;
+}
+
+void cleanup_module(void)
+{
+    int i;
+
+	WARN(("cleanup_module called\n"));
+	
+    for (i = 0; i < MCDX_NDRIVES; i++) {
+		struct s_drive_stuff *stuffp;
+		stuffp = mcdx_stuffp[i];
+		if (!stuffp) continue;
+		release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE);
+		free_irq(stuffp->irq);
+		if (stuffp->toc) {
+			TRACE((MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc));
+			kfree(stuffp->toc);
+		}
+		TRACE((MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp));
+		mcdx_stuffp[i] = NULL;
+		kfree(stuffp);
+    }
+
+    if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) 
+        WARN(("cleanup() unregister_blkdev() failed\n"));
+    else INFO(("cleanup() succeeded\n"));
+}
+
+#endif MODULE
+
+/* Support functions ************************************************/
+
+#if MCDX_DEBUG
+void trace(int level, const char* fmt, ...)
+{
+	char s[255];
+	va_list args;
+	if (level < 1) return;
+	va_start(args, fmt);
+	if (sizeof(s) < vsprintf(s, fmt, args))
+		printk(MCDX ":: dprintf exeeds limit!!\n");
+	else printk(MCDX ":: %s", s);
+	va_end(args);
+}
+#endif
+
+void warn(const char* fmt, ...)
+{
+	char s[255];
+	va_list args;
+	va_start(args, fmt);
+	if (sizeof(s) < vsprintf(s, fmt, args))
+		printk(MCDX ":: dprintf exeeds limit!!\n");
+	else printk(MCDX ": %s", s);
+	va_end(args);
+}
+
+
+int mcdx_init(void)
+{
+	int drive;
+
+	INFO((": Version 1.0a "
+			"mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp\n"));
+
+	/* zero the pointer array */
+	for (drive = 0; drive < MCDX_NDRIVES; drive++)
+		mcdx_stuffp[drive] = NULL;
+
+	/* do the initialisation */
+	for (drive = 0; drive < MCDX_NDRIVES; drive++) { 
+		struct s_version version;
+		struct s_drive_stuff* stuffp;
+        int size;
+
+        size = sizeof(*stuffp);
+		
+		TRACE((INIT, "init() try drive %d\n", drive));
+
+		TRACE((MALLOC, "init() malloc %d bytes\n", size));
+		if (!(stuffp = kmalloc(size, GFP_KERNEL))) {
+			WARN(("init() malloc failed\n"));
+			break; 
+		}
+
+		TRACE((INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp));
+
+		/* zero the stuff */
+		memset(stuffp, 0, sizeof(*stuffp));
+
+		stuffp->present = 0;		/* this should be 0 already */
+		stuffp->toc = NULL;			/* this should be NULL already */
+		stuffp->changed = jiffies;
+
+		/* setup our irq and i/o addresses */
+		stuffp->irq = irq(mcdx_drive_map[drive]);
+		stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]);
+		stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
+		stuffp->wreg_hcon = stuffp->wreg_reset + 1;
+		stuffp->wreg_chn = stuffp->wreg_hcon + 1;
+
+		/* check if i/o addresses are available */
+		if (0 != check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
+            WARN(("%s=0x%3p,%d: "
+                    "Init failed. I/O ports (0x%3p..0x3p) already in use.\n"
+                    MCDX, 
+                    stuffp->wreg_data, stuffp->irq,
+                    stuffp->wreg_data, 
+                    stuffp->wreg_data + MCDX_IO_SIZE - 1));
+			TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
+            kfree(stuffp);
+			TRACE((INIT, "init() continue at next drive\n"));
+			continue; /* next drive */
+		}
+
+		TRACE((INIT, "init() i/o port is available at 0x%3p\n", stuffp->wreg_data));
+
+		TRACE((INIT, "init() hardware reset\n"));
+		mcdx_reset(stuffp, HARD, 1);
+
+		TRACE((INIT, "init() get version\n"));
+		if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
+			/* failed, next drive */
+            WARN(("%s=0x%3p,%d: Init failed. Can't get version.\n",
+                    MCDX,
+                    stuffp->wreg_data, stuffp->irq));
+			TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp));
+            kfree(stuffp);
+			TRACE((INIT, "init() continue at next drive\n"));
+			continue;
+		}
+
+		switch (version.code) {
+		case 'D': 
+                stuffp->readcmd = READDSPEED; 
+                stuffp->present = DOUBLE | DOOR | MULTI; 
+                break;
+		case 'F': 
+                stuffp->readcmd = READSSPEED; 
+                stuffp->present = SINGLE | DOOR | MULTI;
+                break;
+		case 'M': 
+                stuffp->readcmd = READSSPEED;
+                stuffp->present = SINGLE;
+                break;
+		default: 
+                stuffp->present = 0; break;
+		}
+
+        stuffp->playcmd = READSSPEED;
+
+
+		if (!stuffp->present) {
+            WARN(("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
+                    MCDX, stuffp->wreg_data, stuffp->irq));
+			kfree(stuffp);
+			continue; /* next drive */
+		}
+
+		TRACE((INIT, "init() register blkdev\n"));
+		if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) {
+            WARN(("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
+                    MCDX,
+                    stuffp->wreg_data, stuffp->irq, MAJOR_NR));
+			kfree(stuffp);
+			continue; /* next drive */
+		}
+
+		blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+		read_ahead[MAJOR_NR] = READ_AHEAD;
+
+#if WE_KNOW_WHY
+		 blksize_size[MAJOR_NR] = BLKSIZES;
+#endif
+
+		TRACE((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)) {
+            WARN(("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
+                    MCDX,
+                    stuffp->wreg_data, stuffp->irq, stuffp->irq));
+			stuffp->irq = 0;
+			kfree(stuffp);
+			continue;
+		}
+		request_region((unsigned int) stuffp->wreg_data, 
+                MCDX_IO_SIZE, 
+                DEVICE_NAME); 
+
+		TRACE((INIT, "init() get garbage\n"));
+		{
+			int i;
+			mcdx_delay(stuffp, HZ/2);
+			for (i = 100; i; i--) (void) inb((unsigned int) stuffp->rreg_status);
+		}
+
+
+#if WE_KNOW_WHY
+			outb(0x50, (unsigned int) stuffp->wreg_chn);	/* irq 11 -> channel register */
+#endif
+
+		TRACE((INIT, "init() set non dma but irq mode\n"));
+		mcdx_config(stuffp, 1);
+
+		stuffp->minor = drive;
+
+		WARN((DEVICE_NAME " installed at 0x%3p, irq %d."
+			   " (Firmware version %c %x)\n",
+			   stuffp->wreg_data, stuffp->irq, version.code,
+               version.ver));
+		mcdx_stuffp[drive] = stuffp;
+		TRACE((INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp));
+	}
+
+	return 0;
+}
+
+
+static int mcdx_transfer(struct s_drive_stuff *stuffp,
+		char *p, int sector, int nr_sectors)
+/*	This does actually the transfer from the drive.
+	Return:	-1 on timeout or other error
+			else status byte (as in stuff->st) */
+{
+    int off;
+    int done = 0;
+
+	TRACE((TRANSFER, "transfer() %d sectors at sector %d\n",
+			nr_sectors, sector));
+
+	if (stuffp->audio) {
+		WARN(("attempt to read from audio cd\n"));
+		return -1;
+	}
+
+    while (stuffp->lock)
+		interruptible_sleep_on(&stuffp->lockq);
+    if (current->signal && ~current->blocked) {
+        WARN(("talk() got signal %d\n", current->signal));
+    }
+
+    if (stuffp->valid
+			&& (sector >= stuffp->pending)
+			&& (sector < stuffp->off_direct)) {
+
+
+	off = stuffp->off_requested < (off = sector + nr_sectors)
+			? stuffp->off_requested : off;
+
+	stuffp->lock = current->pid;
+
+	do {
+	    int sig = 0;
+	    int to = 0;
+
+		/* wait for the drive become idle */
+	    current->timeout = jiffies + 5*HZ;
+	    while (stuffp->busy) {
+			interruptible_sleep_on(&stuffp->busyq);
+			if ((sig = (current->signal && ~current->blocked))
+					|| (to = (current->timeout == 0))) {
+				break;
+			} 	
+		}
+
+	    current->timeout = 0;
+
+		/* test for possible errors */
+	    if (((stuffp->busy == 0) && !stuffp->introk)
+				|| sig
+				|| to) {
+			if ((stuffp->busy == 0) && !stuffp->introk)
+                WARN(("mcdx_transfer() failure in data request\n"));
+			else if (to)
+                WARN(("mcdx_transfer(): timeout\n"));
+			else if (sig)
+                WARN(("mcdx_transfer(): got signal 0x%lx\n", current->signal));
+
+			stuffp->lock = 0;
+			stuffp->busy = 0;
+			wake_up_interruptible(&stuffp->lockq);
+			wake_up_interruptible(&stuffp->busyq);
+			stuffp->errno = MCDX_E;
+			TRACE((TRANSFER, "transfer() done (-1)\n"));
+			return -1;
+	    }
+
+		/* test if it's the first sector of a block,
+		 * there we have to skip some bytes as we read raw data */
+		if (stuffp->xa && (0 == (stuffp->pending & 3))) {
+			const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE;
+			TRACE((TRANSFER, "transfer() sector %d, skip %d header bytes\n",
+				stuffp->pending, HEAD));
+			insb((unsigned int) stuffp->rreg_data, p, HEAD);
+		}
+
+		/* now actually read the data */
+
+		TRACE((TRANSFER, "transfer() read sector %d\n", stuffp->pending));
+	    insb((unsigned int) stuffp->rreg_data, p, 512); 
+
+		/* test if it's the last sector of a block,
+		 * if so, we have to expect an interrupt and to skip some
+		 * data too */
+		if ((stuffp->busy = (3 == (stuffp->pending & 3))) && stuffp->xa) {
+			char dummy[CD_XA_TAIL];
+			TRACE((TRANSFER, "transfer() sector %d, skip %d trailer bytes\n",
+					stuffp->pending, CD_XA_TAIL));
+			insb((unsigned int) stuffp->rreg_data, &dummy[0], CD_XA_TAIL);
+		}
+
+	    if (stuffp->pending == sector) {
+			p += 512;
+			done++;
+			sector++;
+	    }
+	}
+	while (++(stuffp->pending) < off);
+
+	stuffp->lock = 0;
+	wake_up_interruptible(&stuffp->lockq);
+
+    } else {
+
+		static unsigned char cmd[] = {
+			0,
+			0, 0, 0,
+			0, 0, 0
+		};
+
+		cmd[0] = stuffp->readcmd;
+
+		stuffp->valid = 1;
+		stuffp->pending = sector & ~3;
+
+		/* do some sanity checks */
+		TRACE((TRANSFER, "transfer() request sector %d\n", stuffp->pending));
+		if (stuffp->pending > stuffp->lastsector) {
+			WARN(("transfer() sector %d from nirvana requested.\n",
+				stuffp->pending));
+			stuffp->errno = MCDX_EOM;
+			TRACE((TRANSFER, "transfer() done (-1)\n"));
+			return -1;
+		}
+
+		if ((stuffp->off_direct = stuffp->pending + DIRECT_SIZE)
+			> stuffp->lastsector + 1)
+			stuffp->off_direct = stuffp->lastsector + 1;
+		if ((stuffp->off_requested = stuffp->pending + REQUEST_SIZE)
+			> stuffp->lastsector + 1)
+			stuffp->off_requested = stuffp->lastsector + 1;
+
+		TRACE((TRANSFER, "transfer() pending %d\n", stuffp->pending));
+		TRACE((TRANSFER, "transfer() off_dir %d\n", stuffp->off_direct));
+		TRACE((TRANSFER, "transfer() off_req %d\n", stuffp->off_requested));
+
+		{
+			struct s_msf pending;
+			log2msf(stuffp->pending / 4, &pending);
+			cmd[1] = pending.minute;
+			cmd[2] = pending.second;
+			cmd[3] = pending.frame;
+		}
+
+		stuffp->busy = 1;
+		cmd[6] = (unsigned char) (stuffp->off_requested - stuffp->pending) / 4;
+
+		outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
+
+    }
+
+    stuffp->off_direct = (stuffp->off_direct += done) < stuffp->off_requested
+	    ? stuffp->off_direct : stuffp->off_requested;
+
+	TRACE((TRANSFER, "transfer() done (%d)\n", done));
+    return done;
+}
+
+
+/*	Access to elements of the mcdx_drive_map members */
+
+static char* port(int *ip) { return (char*) ip[0]; }
+static int irq(int *ip) { return ip[1]; }
+
+/*	Misc number converters */
+
+static unsigned int bcd2uint(unsigned char c)
+{ return (c >> 4) * 10 + (c & 0x0f); }
+
+static unsigned int uint2bcd(unsigned int ival)
+{ return ((ival / 10) << 4) | (ival % 10); }
+
+static void log2msf(unsigned int l, struct s_msf* pmsf)
+{
+    l += CD_BLOCK_OFFSET;
+    pmsf->minute = uint2bcd(l / 4500), l %= 4500;
+    pmsf->second = uint2bcd(l / 75);
+    pmsf->frame = uint2bcd(l % 75);
+}
+
+static unsigned int msf2log(const struct s_msf* pmsf)
+{
+    return bcd2uint(pmsf->frame)
+    + bcd2uint(pmsf->second) * 75
+    + bcd2uint(pmsf->minute) * 4500
+    - CD_BLOCK_OFFSET;
+}
+	
+int mcdx_readtoc(struct s_drive_stuff* stuffp)
+/*  Read the toc entries from the CD,
+ *  Return: -1 on failure, else 0 */
+{
+
+	if (stuffp->toc) {
+		TRACE((IOCTL, "ioctl() toc already read\n"));
+		return 0;
+	}
+
+	TRACE((IOCTL, "ioctl() readtoc for %d tracks\n",
+			stuffp->di.n_last - stuffp->di.n_first + 1));
+
+    if (-1 == mcdx_hold(stuffp, 1)) return -1;
+
+	TRACE((IOCTL, "ioctl() tocmode\n"));
+	if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO;
+
+	/* all seems to be ok so far ... malloc */
+	{
+		int size;
+		size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2);
+
+		TRACE((MALLOC, "ioctl() malloc %d bytes\n", size));
+		stuffp->toc = kmalloc(size, GFP_KERNEL);
+		if (!stuffp->toc) {
+			WARN(("Cannot malloc %s bytes for toc\n", size));
+			mcdx_setdrivemode(stuffp, DATA, 1);
+			return -EIO;
+		}
+	}
+
+	/* now read actually the index */
+	{
+		int trk;
+		int retries;
+
+		for (trk = 0; 
+				trk < (stuffp->di.n_last - stuffp->di.n_first + 1); 
+				trk++)
+			stuffp->toc[trk].index = 0;
+
+		for (retries = 300; retries; retries--) { /* why 300? */
+			struct s_subqcode q;
+			unsigned int idx;
+		
+			if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) {
+				mcdx_setdrivemode(stuffp, DATA, 1);
+				return -EIO;
+			}
+
+			idx = bcd2uint(q.index);
+
+			if ((idx > 0) 
+					&& (idx <= stuffp->di.n_last) 
+					&& (q.tno == 0)
+					&& (stuffp->toc[idx - stuffp->di.n_first].index == 0)) {
+				stuffp->toc[idx - stuffp->di.n_first] = q;
+				TRACE((IOCTL, "ioctl() toc idx %d (trk %d)\n", idx, trk));
+				trk--;
+			}
+			if (trk == 0) break;
+		}
+		memset(&stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1], 
+				0, sizeof(stuffp->toc[0]));
+		stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt
+				= stuffp->di.msf_leadout;
+	}
+
+	/* unset toc mode */
+	TRACE((IOCTL, "ioctl() undo toc mode\n"));
+	if (-1 == mcdx_setdrivemode(stuffp, DATA, 2))
+		return -EIO;
+
+#if MCDX_DEBUG && IOCTL
+	{ int trk;
+	for (trk = 0; 
+			trk < (stuffp->di.n_last - stuffp->di.n_first + 2); 
+			trk++)
+		TRACE((IOCTL, "ioctl() %d readtoc %02x %02x %02x"
+				"  %02x:%02x.%02x  %02x:%02x.%02x\n",
+				trk + stuffp->di.n_first,
+				stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index,
+				stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame,
+				stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame));
+	}
+#endif
+
+	return 0;
+}
+
+static int
+mcdx_playmsf(struct s_drive_stuff* stuffp, const struct cdrom_msf* msf)
+{
+    unsigned char cmd[7] = {
+        0, 0, 0, 0, 0, 0, 0
+    };
+
+    cmd[0] = stuffp->playcmd;
+    
+    cmd[1] = msf->cdmsf_min0;
+    cmd[2] = msf->cdmsf_sec0;
+    cmd[3] = msf->cdmsf_frame0;
+    cmd[4] = msf->cdmsf_min1;
+    cmd[5] = msf->cdmsf_sec1;
+    cmd[6] = msf->cdmsf_frame1;
+
+    TRACE((IOCTL, "ioctl(): play %x "
+            "%02x:%02x:%02x -- %02x:%02x:%02x\n",
+            cmd[0], cmd[1], cmd[2], cmd[3],
+            cmd[4], cmd[5], cmd[6])); 
+
+    outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
+
+    if (-1 == mcdx_getval(stuffp, 1*HZ, 0, NULL)) {
+        WARN(("playmsf() timeout\n")); 
+        return -1;
+    }
+
+    stuffp->audiostatus = CDROM_AUDIO_PLAY;
+    return 0;
+}
+
+static int 
+mcdx_playtrk(struct s_drive_stuff* stuffp, const struct cdrom_ti* ti)
+{
+    struct s_subqcode* p;
+    struct cdrom_msf msf;
+
+    if (-1 == mcdx_readtoc(stuffp)) return -1;
+
+    if (ti) p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first];
+    else p = &stuffp->start;
+
+    msf.cdmsf_min0 = p->dt.minute;
+    msf.cdmsf_sec0 = p->dt.second;
+    msf.cdmsf_frame0 = p->dt.frame;
+
+    if (ti) {
+        p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1];
+        stuffp->stop = *p;
+    } else p = &stuffp->stop;
+
+    msf.cdmsf_min1 = p->dt.minute;
+    msf.cdmsf_sec1 = p->dt.second;
+    msf.cdmsf_frame1 = p->dt.frame;
+
+    return mcdx_playmsf(stuffp, &msf);
+}
+
+
+/* Drive functions ************************************************/
+
+static int 
+mcdx_closedoor(struct s_drive_stuff *stuffp, int tries)
+{
+	if (stuffp->present & DOOR)
+		return mcdx_talk(stuffp, "\xf8", 1, NULL, 0, 5*HZ, tries);
+	else
+		return 0;
+}
+
+static int 
+mcdx_stop(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\xf0", 1, NULL, 0, 2*HZ, tries); }
+
+static int
+mcdx_hold(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\x70", 1, NULL, 0, 2*HZ, tries); }
+
+static int
+mcdx_eject(struct s_drive_stuff *stuffp, int tries)
+{
+	if (stuffp->present & DOOR)
+		return mcdx_talk(stuffp, "\xf6", 1, NULL, 0, 5*HZ, tries);
+	else
+		return 0;
+}
+
+static int
+mcdx_requestsubqcode(struct s_drive_stuff *stuffp, 
+        struct s_subqcode *sub, 
+        int tries)
+{
+	char buf[11];
+	int ans;
+
+	if (-1 == (ans = mcdx_talk(
+            stuffp, "\x20", 1, buf, sizeof(buf),
+            2*HZ, tries))) 
+        return -1;
+	sub->control = buf[1];
+	sub->tno = buf[2];
+	sub->index = buf[3];
+	sub->tt.minute = buf[4];
+	sub->tt.second = buf[5];
+	sub->tt.frame = buf[6];
+	sub->dt.minute = buf[8];
+	sub->dt.second = buf[9];
+	sub->dt.frame = buf[10];
+
+	return ans;
+}
+
+static int
+mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, struct s_multi *multi, int tries)
+{
+	char buf[5];
+	int ans;
+
+    if (stuffp->present & MULTI) {
+        ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2*HZ, tries);
+        multi->multi = buf[1];
+        multi->msf_last.minute = buf[2];
+        multi->msf_last.second = buf[3];
+        multi->msf_last.frame = buf[4];
+        return ans;
+    } else {
+        multi->multi = 0;
+        return 0;
+    }
+}
+
+static int 
+mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, int tries)
+{
+	char buf[9];
+	int ans;
+	ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2*HZ, tries);
+	info->n_first = bcd2uint(buf[1]);
+	info->n_last = bcd2uint(buf[2]);
+	info->msf_leadout.minute = buf[3];
+	info->msf_leadout.second = buf[4];
+	info->msf_leadout.frame = buf[5];
+	info->msf_first.minute = buf[6];
+	info->msf_first.second = buf[7];
+	info->msf_first.frame = buf[8];
+	return ans;
+}
+
+static int
+mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries)
+{
+	char cmd[2];
+	int ans;
+
+	TRACE((HW, "setdrivemode() %d\n", mode));
+
+	if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5*HZ, tries)))
+		return -1;
+
+	switch (mode) {
+	  case TOC: cmd[1] |= 0x04; break;
+	  case DATA: cmd[1] &= ~0x04; break;
+	  case RAW: cmd[1] |= 0x40; break;
+	  case COOKED: cmd[1] &= ~0x40; break;
+	  default: break;
+	}
+	cmd[0] = 0x50;
+	return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries);
+}
+
+
+static int
+mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries)
+{
+	unsigned char cmd[2] = { 0xa0 };
+	TRACE((HW, "setdatamode() %d\n", mode));
+	switch (mode) {
+	  case MODE0: cmd[1] = 0x00; break;
+	  case MODE1: cmd[1] = 0x01; break;
+	  case MODE2: cmd[1] = 0x02; break;
+	  default: return -EINVAL;
+	}
+	return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries);
+}
+
+static int
+mcdx_config(struct s_drive_stuff *stuffp, int tries)
+{
+	char cmd[4];
+
+	TRACE((HW, "config()\n"));
+
+	cmd[0] = 0x90;
+
+	cmd[1] = 0x10;		/* irq enable */
+	cmd[2] = 0x05;		/* pre, err irq enable */
+
+	if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries))
+		return -1;
+
+	cmd[1] = 0x02;		/* dma select */
+	cmd[2] = 0x00;		/* no dma */
+
+	return mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries);
+}
+
+static int
+mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, int tries)
+{
+	char buf[3];
+	int ans;
+
+	if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 1, buf, sizeof(buf), 2*HZ, tries)))
+		return ans;
+
+	ver->code = buf[1];
+	ver->ver = buf[2];
+
+	return ans;
+}
+
+static int
+mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries)
+{ 
+	if (mode == HARD) {
+		outb(0, (unsigned int) stuffp->wreg_chn);		/* no dma, no irq -> hardware */
+		outb(0, (unsigned int) stuffp->wreg_reset);		/* hw reset */
+		return 0;
+	} else return mcdx_talk(stuffp, "\x60", 1, NULL, 0, 5*HZ, tries);
+}
+
+static int
+mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries)
+{
+	char cmd[2] = { 0xfe };
+    if (stuffp->present & DOOR) {
+        cmd[1] = lock ? 0x01 : 0x00;
+        return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 0, 5*HZ, tries);
+    } else 
+        return 0;
+}
+
+static int
+mcdx_getstatus(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\x40", 1, NULL, 0, 5*HZ, tries); }
+
+static int
+mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char* buf)
+{
+    unsigned long timeout = to + jiffies;
+    char c;
+
+    if (!buf) buf = &c;
+
+    while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) {
+        if (jiffies > timeout) return -1;
+        mcdx_delay(stuffp, delay);
+    }
+
+    *buf = (unsigned char) inb((unsigned int) stuffp->rreg_data) & 0xff;
+
+    return 0;
+}
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c
--- v1.3.35/linux/drivers/cdrom/optcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/optcd.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,1669 @@
+/*	$Id: optcd.c,v 1.3 1995/08/24 19:54:27 root Exp root $
+	linux/drivers/block/optcd.c - Optics Storage 8000 AT CDROM driver
+
+	Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
+
+	Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks
+	by Eberhard Moenkeberg (emoenke@gwdg.de). ISP16 detection and
+	configuration by Eric van der Maarel (maarel@marin.nl), with some data
+	communicated by Vadim V. Model (vadim@rbrf.msk.su).
+
+	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; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	History
+	14-5-95		v0.0	Plays sound tracks. No reading of data CDs yet.
+				Detection of disk change doesn't work.
+	21-5-95		v0.1	First ALPHA version. CD can be mounted. The
+				device major nr is borrowed from the Aztech
+				driver. Speed is around 240 kb/s, as measured
+				with "time dd if=/dev/cdrom of=/dev/null \
+				bs=2048 count=4096".
+	24-6-95		v0.2	Reworked the #defines for the command codes
+				and the like, as well as the structure of
+				the hardware communication protocol, to
+				reflect the "official" documentation, kindly
+				supplied by C.K. Tan, Optics Storage Pte. Ltd.
+				Also tidied up the state machine somewhat.
+	28-6-95		v0.3	Removed the ISP-16 interface code, as this
+				should go into its own driver. The driver now
+				has its own major nr.
+				Disk change detection now seems to work, too.
+				This version became part of the standard
+				kernel as of version 1.3.7
+	24-9-95		v0.4	Re-inserted ISP-16 interface code which I
+				copied from sjcd.c, with a few changes.
+				Updated README.optcd. Submitted for
+				inclusion in 1.3.21
+	29-9-95		v0.4a	Fixed bug that prevented compilation as module
+*/
+
+#include <linux/major.h>
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+	char kernel_version[]= UTS_RELEASE;
+# endif
+#define optcd_init init_module
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#define MAJOR_NR OPTICS_CDROM_MAJOR
+
+# include <linux/blk.h>
+#define optcd_port optcd	/* Needed for the modutils. */
+# include <linux/optcd.h>
+
+
+/* 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 due to soundcard conflicts */
+#define ISP16_IRQ_7  0x08  /* shouldn't be used due to soundcard 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 drive */
+#define ISP16_NO_IDE__ENABLE_CDROM_PORT  0xF90  /* ISP16 without IDE interface */
+#define ISP16_IDE__ENABLE_CDROM_PORT  0xF91  /* ISP16 with IDE interface */
+#define ISP16_ENABLE_CDROM  0x80  /* seven bit */
+
+/* the magic stuff */
+#define ISP16_CTRL_PORT  0xF8F
+#define ISP16_NO_IDE__CTRL  0xE2  /* ISP16 without IDE interface */
+#define ISP16_IDE__CTRL  0xE3  /* ISP16 with IDE interface */
+
+static short isp16_detect(void);
+static short isp16_no_ide__detect(void);
+static short isp16_with_ide__detect(void);
+static short isp16_config( int base, u_char drive_type, int irq, int dma );
+static short isp16_type; /* dependent on type of interface card */
+static u_char isp16_ctrl;
+static u_short isp16_enable_cdrom_port;
+
+
+static short optcd_port = OPTCD_PORTBASE;
+
+/* Read current status/data availability flags */
+inline static int optFlags(void) {
+	return inb(STATUS_PORT) & FL_STDT;
+}
+
+/* Wait for status available; return TRUE on timeout */
+static int sten_low(void) {
+	int no_status;
+	unsigned long count = 0;
+	while ((no_status = (optFlags() & FL_STEN)))
+		if (++count >= BUSY_TIMEOUT)
+			break;
+#ifdef DEBUG_DRIVE_IF
+	if (no_status)
+		printk("optcd: timeout waiting for STEN low\n");
+	else
+		printk("optcd: STEN low after %ld\n", count);
+#endif
+	return no_status;
+}
+
+/* Wait for data available; return TRUE on timeout */
+static int dten_low(void) {
+	int no_data;
+	unsigned long count = 0;
+	while ((no_data = (optFlags() & FL_DTEN)))
+		if (++count >= BUSY_TIMEOUT)
+			break;
+#ifdef DEBUG_DRIVE_IF
+	if (no_data)
+		printk("optcd: timeout waiting for DTEN low\n");
+	else
+		printk("optcd: DTEN low after %ld\n", count);
+#endif
+	return no_data;
+}
+
+/* Facilities for polled waiting for status or data */
+static int sleep_timeout;		/* Max amount of time still to sleep */
+static unsigned char sleep_flags;	/* Flags read last time around */
+static struct wait_queue *waitq = NULL;
+static struct timer_list delay_timer = {NULL, NULL, 0, 0, NULL};
+
+/* Timer routine: wake up when either of FL_STEN or FL_DTEN goes down,
+ * or when timeout expires. Otherwise wait some more.
+ */
+static void sleep_timer(void) {
+	if ((sleep_flags = optFlags()) != FL_STDT) {
+		wake_up(&waitq);
+		return;
+	}
+	if (--sleep_timeout <= 0) {
+		wake_up(&waitq);
+		return;
+	}
+	SET_TIMER(sleep_timer, 1);
+}
+
+/* Sleep until any of FL_STEN or FL_DTEN go down, or until timeout.
+ * sleep_timeout must be set first.
+ */
+static int sleep_status(void) {
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: sleeping %d on status\n", sleep_timeout);
+#endif
+	if (sleep_timeout <= 0)		/* timeout immediately */
+		return FL_STDT;
+	if ((sleep_flags = optFlags()) == FL_STDT) {
+		SET_TIMER(sleep_timer, 1);
+		sleep_on(&waitq);
+	}
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: woken up with %d to go, flags %d\n",
+		sleep_timeout, sleep_flags);
+#endif
+	return sleep_flags;
+}
+
+/* Sleep until status available; return TRUE on timeout */
+inline static int sleep_sten_low(void) {
+	int flags;
+	sleep_timeout = SLEEP_TIMEOUT;
+	flags = sleep_status();
+#ifdef DEBUG_DRIVE_IF
+	if (!(flags & FL_DTEN))
+		printk("optcd: DTEN while waiting for STEN\n");
+#endif
+	return flags & FL_STEN;
+}
+
+/* Sleep until data available; return TRUE on timeout */
+inline static int sleep_dten_low(void) {
+	int flags;
+	sleep_timeout = SLEEP_TIMEOUT;
+	flags = sleep_status();
+#ifdef DEBUG_DRIVE_IF
+	if (!(flags & FL_STEN))
+		printk("optcd: STEN while waiting for DTEN\n");
+#endif
+	return flags & FL_DTEN;
+}
+
+/* Send command code. Return <0 indicates error */
+static int optSendCmd(int cmd) {
+	unsigned char ack;
+#if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
+	printk("optcd: executing command 0x%02x\n", cmd);
+#endif
+	outb(HCON_DTS, HCON_PORT);	/* Enable Suspend Data Transfer */
+	outb(cmd, COMIN_PORT);		/* Send command code */
+	if (sten_low())			/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	ack = inb(DATA_PORT);		/* read command acknowledge */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: acknowledge code 0x%02x\n", ack);
+#endif
+	outb(HCON_SDRQB, HCON_PORT);	/* Disable Suspend Data Transfer */
+	return ack==ST_OP_OK ? 0 : -ack;
+}
+
+/* Send command parameters. Return <0 indicates error */
+static int optSendParams(struct opt_Play_msf *params) {
+	unsigned char ack;
+#if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
+	printk("optcd: params %02x:%02x:%02x %02x:%02x:%02x\n",
+		params->start.min, params->start.sec, params->start.frame,
+		params->end.min, params->end.sec, params->end.frame);
+#endif
+	outb(params -> start.min, COMIN_PORT);
+	outb(params -> start.sec, COMIN_PORT);
+	outb(params -> start.frame, COMIN_PORT);
+	outb(params -> end.min, COMIN_PORT);
+	outb(params -> end.sec, COMIN_PORT);
+	outb(params -> end.frame, COMIN_PORT);
+	if (sten_low())			/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	ack = inb(DATA_PORT);		/* read command acknowledge */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: acknowledge code 0x%02x\n", ack);
+#endif
+	return ack==ST_PA_OK ? 0 : -ack;
+}
+
+/* Return execution status for quick response commands, i.e. busy wait.
+ * Return value <0 indicates timeout.
+ */
+static int optGetExecStatus(void) {
+	unsigned char exec_status;
+	if (sten_low())			/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	exec_status = inb(DATA_PORT);	/* read command execution status */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: returned execution status: 0x%02x\n", exec_status);
+#endif
+	return exec_status;
+}
+
+/* Return execution status for slow commands. Only use when no data is
+ * expected. Return value <0 indicates timeout.
+ */
+static int optSleepTillExecStatus(void) {
+	unsigned char exec_status;
+	if (sleep_sten_low())		/* Wait for status available */
+		return -ERR_IF_CMD_TIMEOUT;
+	exec_status = inb(DATA_PORT);	/* read command execution status */
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: returned execution status: 0x%02x\n", exec_status);
+#endif
+	return exec_status;
+}
+
+/* Fetch status that has previously been waited for. <0 means not available */
+inline static int optStatus(void) {
+	unsigned char status;
+	if (optFlags() & FL_STEN)
+		return -ERR_IF_NOSTAT;
+	status = inb(DATA_PORT);
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: read status: 0x%02x\n", status);
+#endif
+	return status;
+}
+
+/* Wait for extra byte of data that a command returns */
+static int optGetData(void) {
+	unsigned char data;
+	if (sten_low())
+		return -ERR_IF_DATA_TIMEOUT;
+	data = inb(DATA_PORT);
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: read data: 0x%02x\n", data);
+#endif
+	return data;
+}
+
+/* Read data that has previously been waited for. */
+inline static void optReadData(char *buf, int n) {
+	insb(DATA_PORT, buf, n);
+}
+
+/* Flush status and data fifos */
+inline static void optFlushData(void) {
+	while (optFlags() != FL_STDT)
+		inb(DATA_PORT);
+}
+
+/* Write something to RESET_PORT and wait. Return TRUE upon success. */
+static int optResetDrive(void) {
+	unsigned long count = 0;
+	int flags;
+#ifdef DEBUG_DRIVE_IF
+	printk("optcd: reset drive\n");
+#endif
+	outb(0, RESET_PORT);
+	while (++count < RESET_WAIT)
+		inb(DATA_PORT);
+	count = 0;
+	while ((flags = (inb(STATUS_PORT) & FL_RESET)) != FL_RESET)
+		if (++count >= BUSY_TIMEOUT)
+			break;
+#ifdef DEBUG_DRIVE_IF
+	if (flags == FL_RESET)
+		printk("optcd: drive reset\n");
+	else
+		printk("optcd: reset failed\n");
+#endif
+	if (flags != FL_RESET)
+		return 0;		/* Reset failed */
+	outb(HCON_SDRQB, HCON_PORT);	/* Disable Suspend Data Transfer */
+	return 1;			/* Reset succeeded */
+}
+
+
+/* Command protocol */
+
+/* Send a simple command and wait for response */
+inline static int optCmd(int cmd) {
+	int ack = optSendCmd(cmd);
+	if (ack < 0)
+		return ack;
+	if (cmd < COMFETCH)		/* Quick response command */
+		return optGetExecStatus();
+	else				/* Slow command */
+		return optSleepTillExecStatus();
+}
+
+/* Send a command with parameters and wait for response */
+inline static int optPlayCmd(int cmd, struct opt_Play_msf *params) {
+	int ack = optSendCmd(cmd);
+	if (ack < 0)
+		return ack;
+	if ((ack = optSendParams(params)) < 0)
+		return ack;
+	return optSleepTillExecStatus();
+}
+
+/* Send a command with parameters. Don't wait for the response,
+ * which consists of the data blocks read. */
+inline static int optReadCmd(int cmd, struct opt_Play_msf *params) {
+	int ack = optSendCmd(cmd);
+	if (ack < 0)
+		return ack;
+	return optSendParams(params);
+}
+
+
+/* Address conversion routines */
+
+/* Binary to BCD (2 digits) */
+inline static unsigned char bin2bcd(unsigned char p) {
+#ifdef DEBUG_CONV
+	if (p > 99)
+		printk("optcd: error bin2bcd %d\n", p);
+#endif
+	return (p % 10) | ((p / 10) << 4);
+}
+
+/* Linear address to minute, second, frame form */
+static void hsg2msf(long hsg, struct msf *msf) {
+	hsg += 150;
+	msf -> min = hsg / 4500;
+	hsg %= 4500;
+	msf -> sec = hsg / 75;
+	msf -> frame = hsg % 75;
+#ifdef DEBUG_CONV
+	if (msf -> min >= 70)
+		printk("optcd: Error hsg2msf address Minutes\n");
+	if (msf -> sec >= 60)
+		printk("optcd: Error hsg2msf address Seconds\n");
+	if (msf -> frame >= 75)
+		printk("optcd: Error hsg2msf address Frames\n");
+#endif
+	msf -> min = bin2bcd(msf -> min);	/* convert to BCD */
+	msf -> sec = bin2bcd(msf -> sec);
+	msf -> frame = bin2bcd(msf -> frame);
+}
+
+/* Two BCD digits to binary */
+inline static int bcd2bin(unsigned char bcd) {
+	return (bcd >> 4) * 10 + (bcd & 0x0f);
+}
+
+/* Minute, second, frame address to linear address */
+static long msf2hsg(struct msf *mp) {
+#ifdef DEBUG_CONV
+	if (mp -> min >= 70)
+		printk("optcd: Error msf2hsg address Minutes\n");
+	if (mp -> sec >= 60)
+		printk("optcd: Error msf2hsg address Seconds\n");
+	if (mp -> frame >= 75)
+		printk("optcd: Error msf2hsg address Frames\n");
+#endif
+	return bcd2bin(mp -> frame)
+		+ bcd2bin(mp -> sec) * 75
+		+ bcd2bin(mp -> min) * 4500
+		- 150;
+}
+
+
+/* Drive status and table of contents */
+
+static int optAudioStatus = CDROM_AUDIO_NO_STATUS;
+static char optDiskChanged = 1;
+static char optTocUpToDate = 0;
+static struct opt_DiskInfo DiskInfo;
+static struct opt_Toc Toc[MAX_TRACKS];
+
+/* Get CDROM status, flagging completion of audio play and disk changes. */
+static int optGetStatus(void) {
+	int st;
+	if ((st = optCmd(COMIOCTLISTAT)) < 0)
+		return st;
+	if (st == 0xff)
+		return -ERR_IF_NOSTAT;
+	if (((st & ST_MODE_BITS) != ST_M_AUDIO) &&
+		(optAudioStatus == CDROM_AUDIO_PLAY)) {
+		optAudioStatus = CDROM_AUDIO_COMPLETED;
+	}
+	if (st & ST_DSK_CHG) {
+		optDiskChanged = 1;
+		optTocUpToDate = 0;
+		optAudioStatus = CDROM_AUDIO_NO_STATUS;
+	}
+	return st;
+}
+
+/*
+ * Read the current Q-channel info. Also used for reading the
+ * table of contents.
+ */
+static int optGetQChannelInfo(struct opt_Toc *qp) {
+	int st;
+#ifdef DEBUG_TOC
+	printk("optcd: starting optGetQChannelInfo\n");
+#endif
+	if ((st = optGetStatus()) < 0)
+		return st;
+	if ((st = optCmd(COMSUBQ)) < 0)
+		return st;
+	if ((qp -> ctrl_addr = st = optGetData()), st < 0) return st;
+	if ((qp -> track = st = optGetData()), st < 0) return st;
+	if ((qp -> pointIndex = st = optGetData()), st < 0) return st;
+	if ((qp -> trackTime.min = st = optGetData()), st < 0) return st;
+	if ((qp -> trackTime.sec = st = optGetData()), st < 0) return st;
+	if ((qp -> trackTime.frame = st = optGetData()), st < 0) return st;
+	if ((st = optGetData()) < 0) return st;		/* byte not used */
+	if ((qp -> diskTime.min = st = optGetData()), st < 0) return st;
+	if ((qp -> diskTime.sec = st = optGetData()), st < 0) return st;
+	if ((qp -> diskTime.frame = st = optGetData()), st < 0) return st;
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optGetQChannelInfo\n");
+#endif
+	return 0;
+}
+
+#define QINFO_FIRSTTRACK	0xa0
+#define QINFO_LASTTRACK		0xa1
+#define QINFO_DISKLENGTH	0xa2
+
+static int optGetDiskInfo(void) {
+	int st, limit;
+	unsigned char test = 0;
+	struct opt_Toc qInfo;
+#ifdef DEBUG_TOC
+	printk("optcd: starting optGetDiskInfo\n");
+#endif
+	optDiskChanged = 0;
+	if ((st = optCmd(COMLEADIN)) < 0)
+		return st;
+	for (limit = 300; (limit > 0) && (test != 0x0f); limit--) {
+		if ((st = optGetQChannelInfo(&qInfo)) < 0)
+			return st;
+		switch (qInfo.pointIndex) {
+		case QINFO_FIRSTTRACK:
+			DiskInfo.first = bcd2bin(qInfo.diskTime.min);
+#ifdef DEBUG_TOC
+			printk("optcd: got first: %d\n", DiskInfo.first);
+#endif
+			test |= 0x01;
+			break;
+		case QINFO_LASTTRACK:
+			DiskInfo.last = bcd2bin(qInfo.diskTime.min);
+#ifdef DEBUG_TOC
+			printk("optcd: got last: %d\n", DiskInfo.last);
+#endif
+			test |= 0x02;
+			break;
+		case QINFO_DISKLENGTH:
+			DiskInfo.diskLength.min = qInfo.diskTime.min;
+			DiskInfo.diskLength.sec = qInfo.diskTime.sec-2;
+			DiskInfo.diskLength.frame = qInfo.diskTime.frame;
+#ifdef DEBUG_TOC
+			printk("optcd: got length: %x:%x.%x\n",
+				DiskInfo.diskLength.min,
+				DiskInfo.diskLength.sec,
+				DiskInfo.diskLength.frame);
+#endif
+			test |= 0x04;
+			break;
+		default:
+			if ((test & 0x01)	/* Got no of first track */
+			 && (qInfo.pointIndex == DiskInfo.first)) {
+				/* StartTime of First Track */
+				DiskInfo.firstTrack.min = qInfo.diskTime.min;
+				DiskInfo.firstTrack.sec = qInfo.diskTime.sec;
+				DiskInfo.firstTrack.frame = qInfo.diskTime.frame;
+#ifdef DEBUG_TOC
+			printk("optcd: got start: %x:%x.%x\n",
+				DiskInfo.firstTrack.min,
+				DiskInfo.firstTrack.sec,
+				DiskInfo.firstTrack.frame);
+#endif
+				test |= 0x08;
+			}
+		}
+	}
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optGetDiskInfo\n");
+#endif
+	if (test != 0x0f)
+		return -ERR_TOC_MISSINGINFO;
+	return 0;
+}
+
+static int optGetToc(void) {	/* Presumes we have got DiskInfo */
+	int st, count, px, limit;
+	struct opt_Toc qInfo;
+#ifdef DEBUG_TOC
+	int i;
+	printk("optcd: starting optGetToc\n");
+#endif
+	for (count = 0; count < MAX_TRACKS; count++)
+		Toc[count].pointIndex = 0;
+	if ((st = optCmd(COMLEADIN)) < 0)
+		return st;
+	st = 0;
+	count = DiskInfo.last + 3;
+	for (limit = 300; (limit > 0) && (count > 0); limit--) {
+		if ((st = optGetQChannelInfo(&qInfo)) < 0)
+			break;
+		px = bcd2bin(qInfo.pointIndex);
+		if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+			if (Toc[px].pointIndex == 0) {
+				Toc[px] = qInfo;
+				count--;
+			}
+	}
+	Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optGetToc\n");
+	for (i = 1; i <= DiskInfo.last + 1; i++)
+		printk("i = %3d ctl-adr = %02x track %2d px "
+			"%02x %02x:%02x.%02x %02x:%02x.%02x\n",
+			i, Toc[i].ctrl_addr,
+			Toc[i].track,
+			Toc[i].pointIndex,
+			Toc[i].trackTime.min,
+			Toc[i].trackTime.sec,
+			Toc[i].trackTime.frame,
+			Toc[i].diskTime.min,
+			Toc[i].diskTime.sec,
+			Toc[i].diskTime.frame);
+	for (i = 100; i < 103; i++)
+		printk("i = %3d ctl-adr = %02x track %2d px "
+			"%02x %02x:%02x.%02x %02x:%02x.%02x\n",
+			i, Toc[i].ctrl_addr,
+			Toc[i].track,
+			Toc[i].pointIndex,
+			Toc[i].trackTime.min,
+			Toc[i].trackTime.sec,
+			Toc[i].trackTime.frame,
+			Toc[i].diskTime.min,
+			Toc[i].diskTime.sec,
+			Toc[i].diskTime.frame);
+#endif
+	return count ? -ERR_TOC_MISSINGENTRY : 0;
+}
+
+static int optUpdateToc(void) {
+#ifdef DEBUG_TOC
+	printk("optcd: starting optUpdateToc\n");
+#endif
+	if (optTocUpToDate)
+		return 0;
+	if (optGetDiskInfo() < 0)
+		return -EIO;
+	if (optGetToc() < 0)
+		return -EIO;
+	optTocUpToDate = 1;
+#ifdef DEBUG_TOC
+	printk("optcd: exiting optUpdateToc\n");
+#endif
+	return 0;
+}
+
+
+/* Buffers */
+
+#define OPT_BUF_SIZ		16
+#define OPT_BLOCKSIZE		2048
+#define OPT_BLOCKSIZE_RAW	2336
+#define OPT_BLOCKSIZE_ALL	2646
+#define OPT_NOBUF		-1
+
+/* Buffer for block size conversion. */
+static char opt_buf[OPT_BLOCKSIZE*OPT_BUF_SIZ];
+static volatile int opt_buf_bn[OPT_BUF_SIZ], opt_next_bn;
+static volatile int opt_buf_in = 0, opt_buf_out = OPT_NOBUF;
+
+inline static void opt_invalidate_buffers(void) {
+	int i;
+#ifdef DEBUG_BUFFERS
+	printk("optcd: executing opt_invalidate_buffers\n");
+#endif
+	for (i = 0; i < OPT_BUF_SIZ; i++)
+		opt_buf_bn[i] = OPT_NOBUF;
+	opt_buf_out = OPT_NOBUF;
+}
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+static void opt_transfer(void) {
+#if (defined DEBUG_BUFFERS) || (defined DEBUG_REQUEST)
+	printk("optcd: executing opt_transfer\n");
+#endif
+	if (!CURRENT_VALID)
+		return;
+	while (CURRENT -> nr_sectors) {
+		int bn = CURRENT -> sector / 4;
+		int i, offs, nr_sectors;
+		for (i = 0; i < OPT_BUF_SIZ && opt_buf_bn[i] != bn; ++i);
+#ifdef DEBUG_REQUEST
+		printk("optcd: found %d\n", i);
+#endif
+		if (i >= OPT_BUF_SIZ) {
+			opt_buf_out = OPT_NOBUF;
+			break;
+		}
+		offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+		nr_sectors = 4 - (CURRENT -> sector & 3);
+		if (opt_buf_out != i) {
+			opt_buf_out = i;
+			if (opt_buf_bn[i] != bn) {
+				opt_buf_out = OPT_NOBUF;
+				continue;
+			}
+		}
+		if (nr_sectors > CURRENT -> nr_sectors)
+			nr_sectors = CURRENT -> nr_sectors;
+		memcpy(CURRENT -> buffer, opt_buf + offs, nr_sectors * 512);
+		CURRENT -> nr_sectors -= nr_sectors;
+		CURRENT -> sector += nr_sectors;
+		CURRENT -> buffer += nr_sectors * 512;
+	}
+}
+
+
+/* State machine for reading disk blocks */
+
+enum opt_state_e {
+	OPT_S_IDLE,	/* 0 */
+	OPT_S_START,	/* 1 */
+	OPT_S_READ,	/* 2 */
+	OPT_S_DATA,	/* 3 */
+	OPT_S_STOP,	/* 4 */
+	OPT_S_STOPPING	/* 5 */
+};
+
+static volatile enum opt_state_e opt_state = OPT_S_IDLE;
+#ifdef DEBUG_STATE
+static volatile enum opt_state_e opt_state_old = OPT_S_STOP;
+static volatile int opt_st_old = 0;
+static volatile long opt_state_n = 0;
+#endif
+
+static volatile int opt_transfer_is_active = 0;
+static volatile int opt_error = 0;	/* do something with this?? */
+static int optTries;			/* ibid?? */
+
+static void opt_poll(void) {
+	static int optTimeout;
+	static volatile int opt_read_count = 1;
+	int st = 0;
+	int loop_ctl = 1;
+	int skip = 0;
+
+	if (opt_error) {
+		printk("optcd: I/O error 0x%02x\n", opt_error);
+		opt_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+		if (optTries == 5)
+			printk("optcd: read block %d failed; audio disk?\n",
+			        opt_next_bn);
+#endif
+		if (!optTries--) {
+			printk("optcd: read block %d failed; Giving up\n",
+			       opt_next_bn);
+			if (opt_transfer_is_active) {
+				optTries = 0;
+				loop_ctl = 0;
+			}
+			if (CURRENT_VALID)
+				end_request(0);
+			optTries = 5;
+		}
+		opt_error = 0;
+		opt_state = OPT_S_STOP;
+	}
+
+	while (loop_ctl)
+	{
+		loop_ctl = 0; /* each case must flip this back to 1 if we want
+		                 to come back up here */
+#ifdef DEBUG_STATE
+		if (opt_state == opt_state_old)
+			opt_state_n++;
+		else {
+			opt_state_old = opt_state;
+			if (++opt_state_n > 1)
+				printk("optcd: %ld times in previous state\n",
+					opt_state_n);
+			printk("optcd: state %d\n", opt_state);
+			opt_state_n = 0;
+		}
+#endif
+		switch (opt_state) {
+		case OPT_S_IDLE:
+			return;
+		case OPT_S_START:
+			if (optSendCmd(COMDRVST))
+				return;
+			opt_state = OPT_S_READ;
+			optTimeout = 3000;
+			break;
+		case OPT_S_READ: {
+			struct opt_Play_msf msf;
+			if (!skip) {
+				if ((st = optStatus()) < 0)
+					break;
+				if (st & ST_DSK_CHG) {
+					optDiskChanged = 1;
+					optTocUpToDate = 0;
+					opt_invalidate_buffers();
+				}
+			}
+			skip = 0;
+			if ((st & ST_DOOR_OPEN) || (st & ST_DRVERR)) {
+				optDiskChanged = 1;
+				optTocUpToDate = 0;
+				printk((st & ST_DOOR_OPEN)
+				       ? "optcd: door open\n"
+				       : "optcd: disk removed\n");
+				if (opt_transfer_is_active) {
+					opt_state = OPT_S_START;
+					loop_ctl = 1;
+					break;
+				}
+				opt_state = OPT_S_IDLE;
+				while (CURRENT_VALID)
+					end_request(0);
+				return;
+			}
+			if (!CURRENT_VALID) {
+				opt_state = OPT_S_STOP;
+				loop_ctl = 1;
+				break;
+			}
+			opt_next_bn = CURRENT -> sector / 4;
+			hsg2msf(opt_next_bn, &msf.start);
+			opt_read_count = OPT_BUF_SIZ;
+			msf.end.min = 0;
+			msf.end.sec = 0;
+			msf.end.frame = opt_read_count;
+#ifdef DEBUG_REQUEST
+			printk("optcd: reading %x:%x.%x %x:%x.%x\n",
+				msf.start.min,
+				msf.start.sec,
+				msf.start.frame,
+				msf.end.min,
+				msf.end.sec,
+				msf.end.frame);
+			printk("optcd: opt_next_bn:%d opt_buf_in:%d opt_buf_out:%d opt_buf_bn:%d\n",
+				opt_next_bn,
+				opt_buf_in,
+				opt_buf_out,
+				opt_buf_bn[opt_buf_in]);
+#endif
+			optReadCmd(COMREAD, &msf);
+			opt_state = OPT_S_DATA;
+			optTimeout = READ_TIMEOUT;
+			break;
+		}
+		case OPT_S_DATA:
+			st = optFlags() & (FL_STEN|FL_DTEN);
+#ifdef DEBUG_STATE
+			if (st != opt_st_old) {
+				opt_st_old = st;
+				printk("optcd: st:%x\n", st);
+			}
+			if (st == FL_STEN)
+				printk("timeout cnt: %d\n", optTimeout);
+#endif
+			switch (st) {
+			case FL_DTEN:
+#ifdef WARN_IF_READ_FAILURE
+				if (optTries == 5)
+					printk("optcd: read block %d failed; audio disk?\n",
+					       opt_next_bn);
+#endif
+				if (!optTries--) {
+					printk("optcd: read block %d failed; Giving up\n",
+					       opt_next_bn);
+					if (opt_transfer_is_active) {
+						optTries = 0;
+						break;
+					}
+					if (CURRENT_VALID)
+						end_request(0);
+					optTries = 5;
+				}
+				opt_state = OPT_S_START;
+				optTimeout = READ_TIMEOUT;
+				loop_ctl = 1;
+			case (FL_STEN|FL_DTEN):
+				break;
+			default:
+				optTries = 5;
+				if (!CURRENT_VALID && opt_buf_in == opt_buf_out) {
+					opt_state = OPT_S_STOP;
+					loop_ctl = 1;
+					break;
+				}
+				if (opt_read_count<=0)
+					printk("optcd: warning - try to read 0 frames\n");
+				while (opt_read_count) {
+					opt_buf_bn[opt_buf_in] = OPT_NOBUF;
+					if (dten_low()) { /* should be no waiting here!?? */
+						printk("read_count:%d CURRENT->nr_sectors:%ld opt_buf_in:%d\n",
+							opt_read_count,
+							CURRENT->nr_sectors,
+							opt_buf_in);
+						printk("opt_transfer_is_active:%x\n",
+							opt_transfer_is_active);
+						opt_read_count = 0;
+						opt_state = OPT_S_STOP;
+						loop_ctl = 1;
+						end_request(0);
+						break;
+					}
+					optReadData(opt_buf+OPT_BLOCKSIZE*opt_buf_in, OPT_BLOCKSIZE);
+					opt_read_count--;
+#ifdef DEBUG_REQUEST
+					printk("OPT_S_DATA; ---I've read data- read_count: %d\n",
+					       opt_read_count);
+					printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
+					       opt_next_bn,
+					       opt_buf_in,
+					       opt_buf_out,
+					       opt_buf_bn[opt_buf_in]);
+#endif
+					opt_buf_bn[opt_buf_in] = opt_next_bn++;
+					if (opt_buf_out == OPT_NOBUF)
+						opt_buf_out = opt_buf_in;
+					opt_buf_in = opt_buf_in + 1 ==
+						OPT_BUF_SIZ ? 0 : opt_buf_in + 1;
+				}
+				if (!opt_transfer_is_active) {
+					while (CURRENT_VALID) {
+						opt_transfer();
+						if (CURRENT -> nr_sectors == 0)
+							end_request(1);
+						else
+							break;
+					}
+				}
+
+				if (CURRENT_VALID
+				    && (CURRENT -> sector / 4 < opt_next_bn ||
+				    CURRENT -> sector / 4 >
+				     opt_next_bn + OPT_BUF_SIZ)) {
+					opt_state = OPT_S_STOP;
+					loop_ctl = 1;
+					break;
+				}
+				optTimeout = READ_TIMEOUT;
+				if (opt_read_count == 0) {
+					opt_state = OPT_S_STOP;
+					loop_ctl = 1;
+					break;
+				}
+			}
+			break;
+		case OPT_S_STOP:
+			if (opt_read_count != 0)
+				printk("optcd: discard data=%x frames\n",
+					opt_read_count);
+			while (opt_read_count != 0) {
+				optFlushData();
+				opt_read_count--;
+			}
+			if (optSendCmd(COMDRVST))
+				return;
+			opt_state = OPT_S_STOPPING;
+			optTimeout = 1000;
+			break;
+		case OPT_S_STOPPING:
+			if ((st = optStatus()) < 0 && optTimeout)
+					break;
+			if ((st != -1) && (st & ST_DSK_CHG)) {
+				optDiskChanged = 1;
+				optTocUpToDate = 0;
+				opt_invalidate_buffers();
+			}
+			if (CURRENT_VALID) {
+				if (st != -1) {
+					opt_state = OPT_S_READ;
+					loop_ctl = 1;
+					skip = 1;
+					break;
+				} else {
+					opt_state = OPT_S_START;
+					optTimeout = 1;
+				}
+			} else {
+				opt_state = OPT_S_IDLE;
+				return;
+			}
+			break;
+		default:
+			printk("optcd: invalid state %d\n", opt_state);
+			return;
+		} /* case */
+	} /* while */
+
+	if (!optTimeout--) {
+		printk("optcd: timeout in state %d\n", opt_state);
+		opt_state = OPT_S_STOP;
+		if (optCmd(COMSTOP) < 0)
+			return;
+	}
+
+	SET_TIMER(opt_poll, 1);
+}
+
+
+static void do_optcd_request(void) {
+#ifdef DEBUG_REQUEST
+	printk("optcd: do_optcd_request(%ld+%ld)\n",
+	       CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+	opt_transfer_is_active = 1;
+	while (CURRENT_VALID) {
+		if (CURRENT->bh) {
+			if (!CURRENT->bh->b_lock)
+				panic(DEVICE_NAME ": block not locked");
+		}
+		opt_transfer();	/* First try to transfer block from buffers */
+		if (CURRENT -> nr_sectors == 0) {
+			end_request(1);
+		} else {	/* Want to read a block not in buffer */
+			opt_buf_out = OPT_NOBUF;
+			if (opt_state == OPT_S_IDLE) {
+				/* Should this block the request queue?? */
+				if (optUpdateToc() < 0) {
+					while (CURRENT_VALID)
+						end_request(0);
+					break;
+				}
+				/* Start state machine */
+				opt_state = OPT_S_START;
+				optTries = 5;
+				SET_TIMER(opt_poll, 1);	/* why not start right away?? */
+			}
+			break;
+		}
+	}
+	opt_transfer_is_active = 0;
+#ifdef DEBUG_REQUEST
+	printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
+	       opt_next_bn, opt_buf_in, opt_buf_out, opt_buf_bn[opt_buf_in]);
+	printk("optcd: do_optcd_request ends\n");
+#endif
+}
+
+
+/* VFS calls */
+
+static int opt_ioctl(struct inode *ip, struct file *fp,
+			unsigned int cmd, unsigned long arg) {
+	static struct opt_Play_msf opt_Play;	/* pause position */
+	int err;
+#ifdef DEBUG_VFS
+	printk("optcd: starting opt_ioctl, command 0x%x\n", cmd);
+#endif
+	if (!ip)
+		return -EINVAL;
+	if (optGetStatus() < 0)
+		return -EIO;
+	if ((err = optUpdateToc()) < 0)
+		return err;
+
+	switch (cmd) {
+	case CDROMPAUSE: {
+		struct opt_Toc qInfo;
+
+		if (optAudioStatus != CDROM_AUDIO_PLAY)
+			return -EINVAL;
+		if (optGetQChannelInfo(&qInfo) < 0) {
+			/* didn't get q channel info */
+			optAudioStatus = CDROM_AUDIO_NO_STATUS;
+			return 0;
+		}
+		opt_Play.start = qInfo.diskTime;	/* restart point */
+		if (optCmd(COMPAUSEON) < 0)
+			return -EIO;
+		optAudioStatus = CDROM_AUDIO_PAUSED;
+		break;
+	}
+	case CDROMRESUME:
+		if (optAudioStatus != CDROM_AUDIO_PAUSED)
+			return -EINVAL;
+		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
+			optAudioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+		optAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMPLAYMSF: {
+		int st;
+		struct cdrom_msf msf;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
+			return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
+		opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
+		opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
+		opt_Play.end.min = bin2bcd(msf.cdmsf_min1);
+		opt_Play.end.sec = bin2bcd(msf.cdmsf_sec1);
+		opt_Play.end.frame = bin2bcd(msf.cdmsf_frame1);
+		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
+			optAudioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+		optAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	}
+	case CDROMPLAYTRKIND: {
+		int st;
+		struct cdrom_ti ti;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof ti)))
+			return st;
+		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+		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;
+		opt_Play.start = Toc[ti.cdti_trk0].diskTime;
+		opt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+#ifdef DEBUG_VFS
+		printk("optcd: play %02x:%02x.%02x to %02x:%02x.%02x\n",
+			opt_Play.start.min,
+			opt_Play.start.sec,
+			opt_Play.start.frame,
+			opt_Play.end.min,
+			opt_Play.end.sec,
+			opt_Play.end.frame);
+#endif
+		if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
+			optAudioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+		optAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	}
+	case CDROMREADTOCHDR: {		/* Read the table of contents header. */
+		int st;
+		struct cdrom_tochdr tocHdr;
+
+		if ((st = verify_area(VERIFY_WRITE,(void *)arg,sizeof tocHdr)))
+			return st;
+		if (!optTocUpToDate)
+			optGetDiskInfo();
+		tocHdr.cdth_trk0 = DiskInfo.first;
+		tocHdr.cdth_trk1 = DiskInfo.last;
+		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+		break;
+	}
+	case CDROMREADTOCENTRY: {	/* Read a table of contents entry. */
+		int st;
+		struct cdrom_tocentry entry;
+		struct opt_Toc *tocPtr;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof entry)))
+			return st;
+		if ((st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry)))
+			return st;
+		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+		if (!optTocUpToDate)
+			optGetDiskInfo();
+		if (entry.cdte_track == CDROM_LEADOUT)
+			tocPtr = &Toc[DiskInfo.last + 1];
+		else if (entry.cdte_track > DiskInfo.last
+			|| entry.cdte_track < DiskInfo.first)
+			return -EINVAL;
+		else
+			tocPtr = &Toc[entry.cdte_track];
+		entry.cdte_adr = tocPtr -> ctrl_addr;
+		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+		switch (entry.cdte_format) {
+		case CDROM_LBA:
+			entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
+			break;
+		case 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);
+			break;
+		default:
+			return -EINVAL;
+		}
+		memcpy_tofs((void *) arg, &entry, sizeof entry);
+		break;
+	}
+	case CDROMSTOP:
+		optCmd(COMSTOP);
+		optAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMSTART:
+		optCmd(COMCLOSE);	/* What else can we do? */
+		break;
+	case CDROMEJECT:
+		optCmd(COMUNLOCK);
+		optCmd(COMOPEN);
+		break;
+	case CDROMVOLCTRL: {
+		int st;
+		struct cdrom_volctrl volctrl;
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg,
+				sizeof(volctrl))))
+			return st;
+		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+		opt_Play.start.min = 0x10;
+		opt_Play.start.sec = 0x32;
+		opt_Play.start.frame = volctrl.channel0;
+		opt_Play.end.min = volctrl.channel1;
+		opt_Play.end.sec = volctrl.channel2;
+		opt_Play.end.frame = volctrl.channel3;
+		if (optPlayCmd(COMCHCTRL, &opt_Play) < 0)
+			return -EIO;
+		break;
+	}
+	case CDROMSUBCHNL: {	/* Get subchannel info */
+		int st;
+		struct cdrom_subchnl subchnl;
+		struct opt_Toc qInfo;
+
+		if ((st = verify_area(VERIFY_READ,
+				(void *) arg, sizeof subchnl)))
+			return st;
+		if ((st = verify_area(VERIFY_WRITE,
+				(void *) arg, sizeof subchnl)))
+			return st;
+		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
+		if (optGetQChannelInfo(&qInfo) < 0)
+			return -EIO;
+		subchnl.cdsc_audiostatus = optAudioStatus;
+		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);
+		switch (subchnl.cdsc_format) {
+		case CDROM_LBA:
+			subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
+			subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
+			break;
+		case 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);
+			break;
+		default:
+			return -EINVAL;
+		}
+		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
+		break;
+	}
+	case CDROMREADMODE1: {
+		int st;
+		struct cdrom_msf msf;
+		char buf[OPT_BLOCKSIZE];
+
+		if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
+			return st;
+		if ((st = verify_area(VERIFY_WRITE,(void *)arg,OPT_BLOCKSIZE)))
+			return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
+		opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
+		opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
+		opt_Play.end.min = 0;
+		opt_Play.end.sec = 0;
+		opt_Play.end.frame = 1;	/* read only one frame */
+		st = optReadCmd(COMREAD, &opt_Play);
+#ifdef DEBUG_VFS
+		printk("optcd: COMREAD status 0x%x\n", st);
+#endif
+		sleep_dten_low();	/* error checking here?? */
+		optReadData(buf, OPT_BLOCKSIZE);
+		memcpy_tofs((void *) arg, &buf, OPT_BLOCKSIZE);
+		break;
+	}
+	case CDROMMULTISESSION:
+		return -EINVAL; /* unluckily, not implemented yet */
+
+	default:
+		return -EINVAL;
+	}
+#ifdef DEBUG_VFS
+	printk("optcd: exiting opt_ioctl\n");
+#endif
+	return 0;
+}
+
+static int optPresent = 0;
+static int opt_open_count = 0;
+
+/* Open device special file; check that a disk is in. */
+static int opt_open(struct inode *ip, struct file *fp) {
+#ifdef DEBUG_VFS
+	printk("optcd: starting opt_open\n");
+#endif
+	if (!optPresent)
+		return -ENXIO;		/* no hardware */
+	if (!opt_open_count && opt_state == OPT_S_IDLE) {
+		int st;
+		opt_invalidate_buffers();
+		if ((st = optGetStatus()) < 0)
+			return -EIO;
+		if (st & ST_DOOR_OPEN) {
+			optCmd(COMCLOSE);			/* close door */
+			if ((st = optGetStatus()) < 0)		/* try again */
+				return -EIO;
+		}
+		if (st & (ST_DOOR_OPEN|ST_DRVERR)) {
+			printk("optcd: no disk or door open\n");
+			return -EIO;
+		}
+		if (optUpdateToc() < 0)
+			return -EIO;
+	}
+	opt_open_count++;
+	MOD_INC_USE_COUNT;
+	optCmd(COMLOCK);		/* Lock door */
+#ifdef DEBUG_VFS
+	printk("optcd: exiting opt_open\n");
+#endif
+	return 0;
+}
+
+/* Release device special file; flush all blocks from the buffer cache */
+static void opt_release(struct inode *ip, struct file *fp) {
+#ifdef DEBUG_VFS
+	printk("optcd: executing opt_release\n");
+	printk("inode: %p, inode -> i_rdev: 0x%x, file: %p\n",
+		ip, ip -> i_rdev, fp);
+#endif
+	if (!--opt_open_count) {
+		opt_invalidate_buffers();
+		sync_dev(ip -> i_rdev);
+		invalidate_buffers(ip -> i_rdev);
+		CLEAR_TIMER;
+		optCmd(COMUNLOCK);	/* Unlock door */
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+
+/* Initialisation */
+
+static int version_ok(void) {
+	char devname[100];
+	int count, i, ch;
+
+	if (optCmd(COMVERSION) < 0)
+		return 0;
+	if ((count = optGetData()) < 0)
+		return 0;
+	for (i = 0, ch = -1; count > 0; count--) {
+		if ((ch = optGetData()) < 0)
+			break;
+		if (i < 99)
+			devname[i++] = ch;
+	}
+	devname[i] = '\0';
+	if (ch < 0)
+		return 0;
+	printk("optcd: Device %s detected\n", devname);
+	return ((devname[0] == 'D')
+	     && (devname[1] == 'O')
+	     && (devname[2] == 'L')
+	     && (devname[3] == 'P')
+	     && (devname[4] == 'H')
+	     && (devname[5] == 'I')
+	     && (devname[6] == 'N'));
+}
+
+
+static struct file_operations opt_fops = {
+	NULL,		/* lseek - default */
+	block_read,	/* read - general block-dev read */
+	block_write,	/* write - general block-dev write */
+	NULL,		/* readdir - bad */
+	NULL,		/* select */
+	opt_ioctl,	/* ioctl */
+	NULL,		/* mmap */
+	opt_open,	/* open */
+	opt_release,	/* release */
+	NULL,		/* fsync */
+	NULL,		/* fasync */
+	NULL,		/* media change */
+	NULL		/* revalidate */
+};
+
+
+/* Get kernel parameter when used as a kernel driver */
+void optcd_setup(char *str, int *ints) {
+	if (ints[0] > 0)
+		optcd_port = ints[1];
+}
+
+/*
+ * Test for presence of drive and initialize it. Called at boot time.
+ */
+
+int optcd_init(void) {
+	if (optcd_port <= 0) {
+		printk("optcd: no Optics Storage CDROM Initialization\n");
+		return -EIO;
+	}
+	if (check_region(optcd_port, 4)) {
+		printk("optcd: conflict, I/O port 0x%x already used\n",
+			optcd_port);
+		return -EIO;
+	}
+
+	if (!check_region(ISP16_DRIVE_SET_PORT, 5)) {
+	/* If someone else has'nt already reserved these ports,
+	   probe for an ISP16 interface card, and enable SONY mode
+	   with no interrupts and no DMA. (As far as I know, all optics
+	   drives come with a SONY interface.) */
+  if ( (isp16_type=isp16_detect()) < 0 )
+    printk( "No ISP16 cdrom interface found.\n" );
+  else {
+    u_char expected_drive;
+
+    printk( "ISP16 cdrom interface (%s optional IDE) detected.\n",
+      (isp16_type==2)?"with":"without" );
+
+    expected_drive = (isp16_type?ISP16_SANYO1:ISP16_SANYO0);
+
+    if ( isp16_config( optcd_port, ISP16_SONY, 0, 0 ) < 0 ) {
+      printk( "ISP16 cdrom interface has not been properly configured.\n" );
+      return -EIO;
+    }
+  }
+	}
+
+	if (!optResetDrive()) {
+		printk("optcd: drive at 0x%x not ready\n", optcd_port);
+		return -EIO;
+	}
+	if (!version_ok()) {
+		printk("optcd: unknown drive detected; aborting\n");
+		return -EIO;
+	}
+	if (optCmd(COMINITDOUBLE) < 0) {
+		printk("optcd: cannot init double speed mode\n");
+		return -EIO;
+	}
+	if (register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
+	{
+		printk("optcd: unable to get major %d\n", MAJOR_NR);
+		return -EIO;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+	request_region(optcd_port, 4, "optcd");
+	optPresent = 1;
+	printk("optcd: 8000 AT CDROM at 0x%x\n", optcd_port);
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void) {
+	if (MOD_IN_USE) {
+		printk("optcd: module in use - can't remove it.\n");
+	return;
+	}
+	if ((unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL)) {
+		printk("optcd: what's that: can't unregister\n");
+		return;
+	}
+	release_region(optcd_port, 4);
+	printk("optcd: module released.\n");
+}
+#endif MODULE
+
+
+/*
+ * -- ISP16 detection and configuration
+ *
+ *    Copyright (c) 1995, Eric van der Maarel <maarel@marin.nl>
+ *
+ *    Version 0.5
+ *
+ *    Detect cdrom interface on ISP16 soundcard.
+ *    Configure cdrom interface.
+ *
+ *    Algorithm for the card with no IDE support option taken
+ *    from the CDSETUP.SYS driver for MSDOS,
+ *    by OPTi Computers, version 2.03.
+ *    Algorithm for the IDE supporting ISP16 as communicated
+ *    to me by Vadim Model and Leo Spiekman.
+ *
+ *    Use, modifification or redistribution of this software is
+ *    allowed under the terms of the GPL.
+ *
+ */
+
+
+#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p))
+#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p))
+
+static short
+isp16_detect(void)
+{
+
+  if ( !( isp16_with_ide__detect() < 0 ) )
+    return(2);
+  else
+    return( isp16_no_ide__detect() );
+}
+
+static short
+isp16_no_ide__detect(void)
+{
+  u_char ctrl;
+  u_char enable_cdrom;
+  u_char io;
+  short i = -1;
+
+  isp16_ctrl = ISP16_NO_IDE__CTRL;
+  isp16_enable_cdrom_port = ISP16_NO_IDE__ENABLE_CDROM_PORT;
+
+  /* read' and write' are a special read and write, respectively */
+
+  /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */
+  ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC;
+  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+
+  /* read' 3,4 and 5-bit from the cdrom enable port */
+  enable_cdrom = ISP16_IN( ISP16_NO_IDE__ENABLE_CDROM_PORT ) & 0x38;
+
+  if ( !(enable_cdrom & 0x20) ) {  /* 5-bit not set */
+    /* read' last 2 bits of ISP16_IO_SET_PORT */
+    io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03;
+    if ( ((io&0x01)<<1) == (io&0x02) ) {  /* bits are the same */
+      if ( io == 0 ) {  /* ...the same and 0 */
+        i = 0;
+        enable_cdrom |= 0x20;
+      }
+      else {  /* ...the same and 1 */  /* my card, first time 'round */
+        i = 1;
+        enable_cdrom |= 0x28;
+      }
+      ISP16_OUT( ISP16_NO_IDE__ENABLE_CDROM_PORT, enable_cdrom );
+    }
+    else {  /* bits are not the same */
+      ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+      return(i); /* -> not detected: possibly incorrect conclusion */
+    }
+  }
+  else if ( enable_cdrom == 0x20 )
+    i = 0;
+  else if ( enable_cdrom == 0x28 )  /* my card, already initialised */
+    i = 1;
+
+  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+
+  return(i);
+}
+
+static short
+isp16_with_ide__detect(void)
+{
+  u_char ctrl;
+  u_char tmp;
+
+  isp16_ctrl = ISP16_IDE__CTRL;
+  isp16_enable_cdrom_port = ISP16_IDE__ENABLE_CDROM_PORT;
+
+  /* read' and write' are a special read and write, respectively */
+
+  /* read' ISP16_CTRL_PORT and save */
+  ctrl = ISP16_IN( ISP16_CTRL_PORT );
+
+  /* write' zero to the ctrl port and get response */
+  ISP16_OUT( ISP16_CTRL_PORT, 0 );
+  tmp = ISP16_IN( ISP16_CTRL_PORT );
+
+  if ( tmp != 2 )  /* isp16 with ide option not detected */
+    return(-1);
+
+  /* restore ctrl port value */
+  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+  
+  return(2);
+}
+
+static short
+isp16_config( int base, u_char drive_type, int irq, int dma )
+{
+  u_char base_code;
+  u_char irq_code;
+  u_char dma_code;
+  u_char i;
+
+  if ( (drive_type == ISP16_MITSUMI) && (dma != 0) )
+    printk( "Mitsumi cdrom drive has no dma support.\n" );
+
+  switch (base) {
+  case 0x340: base_code = ISP16_BASE_340; break;
+  case 0x330: base_code = ISP16_BASE_330; break;
+  case 0x360: base_code = ISP16_BASE_360; break;
+  case 0x320: base_code = ISP16_BASE_320; break;
+  default:
+    printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base );
+    return(-1);
+  }
+  switch (irq) {
+  case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */
+  case 5: irq_code = ISP16_IRQ_5;
+          printk( "Irq 5 shouldn't be used by cdrom interface on ISP16,"
+            " due to possible conflicts with the soundcard.\n");
+          break;
+  case 7: irq_code = ISP16_IRQ_7;
+          printk( "Irq 7 shouldn't be used by cdrom interface on ISP16,"
+            " due to possible conflicts with the soundcard.\n");
+          break;
+  case 3: irq_code = ISP16_IRQ_3; break;
+  case 9: irq_code = ISP16_IRQ_9; break;
+  case 10: irq_code = ISP16_IRQ_10; break;
+  case 11: irq_code = ISP16_IRQ_11; break;
+  default:
+    printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq );
+    return(-1);
+  }
+  switch (dma) {
+  case 0: dma_code = ISP16_DMA_X; break;  /* disable dma */
+  case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16,"
+            " due to conflict with the soundcard.\n");
+          return(-1); break;
+  case 3: dma_code = ISP16_DMA_3; break;
+  case 5: dma_code = ISP16_DMA_5; break;
+  case 6: dma_code = ISP16_DMA_6; break;
+  case 7: dma_code = ISP16_DMA_7; break;
+  default:
+    printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma );
+    return(-1);
+  }
+
+  if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 &&
+    drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 &&
+    drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI &&
+    drive_type != ISP16_DRIVE_X ) {
+    printk( "Drive type (code 0x%02X) not supported by cdrom"
+     " interface on ISP16.\n", drive_type );
+    return(-1);
+  }
+
+  /* set type of interface */
+  i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK;  /* clear some bits */
+  ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type );
+
+  /* enable cdrom on interface with ide support */
+  if ( isp16_type > 1 )
+    ISP16_OUT( isp16_enable_cdrom_port, ISP16_ENABLE_CDROM );
+
+  /* set base address, irq and dma */
+  i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK;  /* keep some bits */
+  ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code );
+
+  return(0);
+}
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- v1.3.35/linux/drivers/cdrom/sbpcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/sbpcd.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,5422 @@
+/*
+ *  sbpcd.c   CD-ROM device driver for the whole family of IDE-style
+ *            Kotobuki/Matsushita/Panasonic CR-5xx drives for
+ *            SoundBlaster ("Pro" or "16 ASP" or compatible) cards
+ *            and for "no-sound" interfaces like Lasermate and the
+ *            Panasonic CI-101P.
+ *            Also for the Longshine LCS-7260 drive.
+ *            Also for the IBM "External ISA CD-Rom" drive.
+ *            Also for the CreativeLabs CD200 drive (but I still need some
+ *            detailed bug reports).
+ *            Also for the TEAC CD-55A drive.
+ *            Not for Sanyo drives (but sjcd is there...).
+ *            Not for any other Funai drives than E2550UA (="CD200" with "F").
+ *
+ *  NOTE:     This is release 3.9.
+ *
+ *  VERSION HISTORY
+ *
+ *  0.1  initial release, April/May 93, after mcd.c (Martin Harriss)
+ *
+ *  0.2  the "repeat:"-loop in do_sbpcd_request did not check for
+ *       end-of-request_queue (resulting in kernel panic).
+ *       Flow control seems stable, but throughput is not better.  
+ *
+ *  0.3  interrupt locking totally eliminated (maybe "inb" and "outb"
+ *       are still locking) - 0.2 made keyboard-type-ahead losses.
+ *       check_sbpcd_media_change added (to use by isofs/inode.c)
+ *       - but it detects almost nothing.
+ *
+ *  0.4  use MAJOR 25 definitely.
+ *       Almost total re-design to support double-speed drives and
+ *       "naked" (no sound) interface cards ("LaserMate" interface type).
+ *       Flow control should be exact now.
+ *       Don't occupy the SbPro IRQ line (not needed either); will
+ *       live together with Hannu Savolainen's sndkit now.
+ *	 Speeded up data transfer to 150 kB/sec, with help from Kai
+ *       Makisara, the "provider" of the "mt" tape utility.
+ *       Give "SpinUp" command if necessary.
+ *       First steps to support up to 4 drives (but currently only one).
+ *       Implemented audio capabilities - workman should work, xcdplayer
+ *       gives some problems.
+ *       This version is still consuming too much CPU time, and
+ *       sleeping still has to be worked on.
+ *       During "long" implied seeks, it seems possible that a 
+ *       ReadStatus command gets ignored. That gives the message
+ *       "ResponseStatus timed out" (happens about 6 times here during
+ *       a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is
+ *       handled without data error, but it should get done better.
+ *
+ *  0.5  Free CPU during waits (again with help from Kai Makisara).
+ *       Made it work together with the LILO/kernel setup standard.
+ *       Included auto-probing code, as suggested by YGGDRASIL.
+ *       Formal redesign to add DDI debugging.
+ *       There are still flaws in IOCTL (workman with double speed drive).
+ *
+ *  1.0  Added support for all drive IDs (0...3, no longer only 0)
+ *       and up to 4 drives on one controller.
+ *       Added "#define MANY_SESSION" for "old" multi session CDs.
+ *
+ *  1.1  Do SpinUp for new drives, too.
+ *       Revised for clean compile under "old" kernels (0.99pl9).
+ *
+ *  1.2  Found the "workman with double-speed drive" bug: use the driver's
+ *       audio_state, not what the drive is reporting with ReadSubQ.
+ *
+ *  1.3  Minor cleanups.
+ *       Refinements regarding Workman.
+ *
+ *  1.4  Read XA disks (PhotoCDs) with "old" drives, too (but only the first
+ *       session - no chance to fully access a "multi-session" CD).
+ *       This currently still is too slow (50 kB/sec) - but possibly
+ *       the old drives won't do it faster.
+ *       Implemented "door (un)lock" for new drives (still does not work
+ *       as wanted - no lock possible after an unlock).
+ *       Added some debugging printout for the UPC/EAN code - but my drives 
+ *       return only zeroes. Is there no UPC/EAN code written?
+ *
+ *  1.5  Laborate with UPC/EAN code (not better yet).
+ *       Adapt to kernel 1.1.8 change (have to explicitly include
+ *       <linux/string.h> now).
+ *
+ *  1.6  Trying to read audio frames as data. Impossible with the current
+ *       drive firmware levels, as it seems. Awaiting any hint. ;-)
+ *       Changed "door unlock": repeat it until success.
+ *       Changed CDROMSTOP routine (stop somewhat "softer" so that Workman
+ *       won't get confused).
+ *       Added a third interface type: Sequoia S-1000, as used with the SPEA
+ *       Media FX sound card. This interface (usable for Sony and Mitsumi 
+ *       drives, too) needs a special configuration setup and behaves like a 
+ *       LaserMate type after that. Still experimental - I do not have such
+ *       an interface.
+ *       Use the "variable BLOCK_SIZE" feature (2048). But it does only work
+ *       if you give the mount option "block=2048".
+ *       The media_check routine is currently disabled; now that it gets
+ *       called as it should I fear it must get synchronized for not to
+ *       disturb the normal driver's activity.
+ *
+ *  2.0  Version number bumped - two reasons:
+ *       - reading audio tracks as data works now with CR-562 and CR-563. We
+ *       currently do it by an IOCTL (yet has to get standardized), one frame
+ *       at a time; that is pretty slow. But it works.
+ *       - we are maintaining now up to 4 interfaces (each up to 4 drives):
+ *       did it the easy way - a different MAJOR (25, 26, ...) and a different
+ *       copy of the driver (sbpcd.c, sbpcd2.c, sbpcd3.c, sbpcd4.c - only
+ *       distinguished by the value of SBPCD_ISSUE and the driver's name),
+ *       and a common sbpcd.h file.
+ *       Bettered the "ReadCapacity error" problem with old CR-52x drives (the
+ *       drives sometimes need a manual "eject/insert" before work): just
+ *       reset the drive and do again. Needs lots of resets here and sometimes
+ *       that does not cure, so this can't be the solution.
+ *
+ *  2.1  Found bug with multisession CDs (accessing frame 16).
+ *       "read audio" works now with address type CDROM_MSF, too.
+ *       Bigger audio frame buffer: allows reading max. 4 frames at time; this
+ *       gives a significant speedup, but reading more than one frame at once
+ *       gives missing chunks at each single frame boundary.
+ *
+ *  2.2  Kernel interface cleanups: timers, init, setup, media check.
+ *
+ *  2.3  Let "door lock" and "eject" live together.
+ *       Implemented "close tray" (done automatically during open).
+ *
+ *  2.4  Use different names for device registering.
+ *
+ *  2.5  Added "#if EJECT" code (default: enabled) to automatically eject
+ *       the tray during last call to "sbpcd_release".
+ *       Added "#if JUKEBOX" code (default: disabled) to automatically eject
+ *       the tray during call to "sbpcd_open" if no disk is in.
+ *       Turn on the CD volume of "compatible" sound cards, too; just define
+ *       SOUND_BASE (in sbpcd.h) accordingly (default: disabled).
+ *
+ *  2.6  Nothing new.  
+ *
+ *  2.7  Added CDROMEJECT_SW ioctl to set the "EJECT" behavior on the fly:
+ *       0 disables, 1 enables auto-ejecting. Useful to keep the tray in
+ *       during shutdown.
+ *
+ *  2.8  Added first support (still BETA, I need feedback or a drive) for
+ *       the Longshine LCS-7260 drives. They appear as double-speed drives
+ *       using the "old" command scheme, extended by tray control and door
+ *       lock functions.
+ *       Found (and fixed preliminary) a flaw with some multisession CDs: we
+ *       have to re-direct not only the accesses to frame 16 (the isofs
+ *       routines drive it up to max. 100), but also those to the continuation
+ *       (repetition) frames (as far as they exist - currently set fix as
+ *       16..20).
+ *       Changed default of the "JUKEBOX" define. If you use this default,
+ *       your tray will eject if you try to mount without a disk in. Next
+ *       mount command will insert the tray - so, just fill in a disk. ;-)
+ *
+ *  2.9  Fulfilled the Longshine LCS-7260 support; with great help and
+ *       experiments by Serge Robyns.
+ *       First attempts to support the TEAC CD-55A drives; but still not
+ *       usable yet.
+ *       Implemented the CDROMMULTISESSION ioctl; this is an attempt to handle
+ *       multi session CDs more "transparent" (redirection handling has to be
+ *       done within the isofs routines, and only for the special purpose of
+ *       obtaining the "right" volume descriptor; accesses to the raw device
+ *       should not get redirected).
+ *
+ *  3.0  Just a "normal" increment, with some provisions to do it better. ;-)
+ *       Introduced "#define READ_AUDIO" to specify the maximum number of 
+ *       audio frames to grab with one request. This defines a buffer size
+ *       within kernel space; a value of 0 will reserve no such space and
+ *       disable the CDROMREADAUDIO ioctl. A value of 75 enables the reading
+ *       of a whole second with one command, but will use a buffer of more
+ *       than 172 kB.
+ *       Started CD200 support. Drive detection should work, but nothing
+ *       more.
+ *
+ *  3.1  Working to support the CD200 and the Teac CD-55A drives.
+ *       AT-BUS style device numbering no longer used: use SCSI style now.
+ *       So, the first "found" device has MINOR 0, regardless of the
+ *       jumpered drive ID. This implies modifications to the /dev/sbpcd*
+ *       entries for some people, but will help the DAU (german TLA, english:
+ *       "newbie", maybe ;-) to install his "first" system from a CD.
+ *
+ *  3.2  Still testing with CD200 and CD-55A drives.
+ *
+ *  3.3  Working with CD200 support.
+ *
+ *  3.4  Auto-probing stops if an address of 0 is seen (to be entered with
+ *       the kernel command line).
+ *       Made the driver "loadable". If used as a module, "audio copy" is
+ *       disabled, and the internal read ahead data buffer has a reduced size
+ *       of 4 kB; so, throughput may be reduced a little bit with slow CPUs.
+ *
+ *  3.5  Provisions to handle weird photoCDs which have an interrupted
+ *       "formatting" immediately after the last frames of some files: simply
+ *       never "read ahead" with MultiSession CDs. By this, CPU usage may be
+ *       increased with those CDs, and there may be a loss in speed.
+ *       Re-structured the messaging system.
+ *       The "loadable" version no longer has a limited READ_AUDIO buffer
+ *       size.
+ *       Removed "MANY_SESSION" handling for "old" multi session CDs.
+ *       Added "private" IOCTLs CDROMRESET and CDROMVOLREAD.
+ *       Started again to support the TEAC CD-55A drives, now that I found
+ *       the money for "my own" drive. ;-)
+ *       The TEAC CD-55A support is fairly working now.
+ *       I have measured that the drive "delivers" at 600 kB/sec (even with
+ *       bigger requests than the drive's 64 kB buffer can satisfy), but
+ *       the "real" rate does not exceed 520 kB/sec at the moment. 
+ *       Caused by the various changes to build in TEAC support, the timed
+ *       loops are de-optimized at the moment (less throughput with CR-52x
+ *       drives, and the TEAC will give speed only with SBP_BUFFER_FRAMES 64).
+ *
+ *  3.6  Fixed TEAC data read problems with SbPro interfaces.
+ *       Initial size of the READ_AUDIO buffer is 0. Can get set to any size
+ *       during runtime.
+ *
+ *  3.7  Introduced MAX_DRIVES for some poor interface cards (seen with TEAC
+ *       drives) which allow only one drive (ID 0); this avoids repetitive
+ *       detection under IDs 1..3. 
+ *       Elongated cmd_out_T response waiting; necessary for photo CDs with
+ *       a lot of sessions.
+ *       Bettered the sbpcd_open() behavior with TEAC drives.
+ *
+ *  3.8  Elongated max_latency for CR-56x drives.
+ *
+ *  3.9  Finally fixed the long-known SoundScape/SPEA/Sequoia S-1000 interface
+ *       configuration bug.
+ *       Now Corey, Heiko, Ken, Leo, Vadim/Eric & Werner are invited to copy
+ *       the config_spea() routine into their drivers. ;-)
+ *
+ *
+ *  TODO
+ *
+ *     disk change detection
+ *     synchronize multi-activity
+ *        (data + audio + ioctl + disk change, multiple drives)
+ *     implement "read all subchannel data" (96 bytes per frame)
+ *     check if CDROMPLAYMSF can cause a hang
+ *
+ *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
+ *     elaborated speed-up experiments (and the fabulous results!), for
+ *     the "push" towards load-free wait loops, and for the extensive mail
+ *     thread which brought additional hints and bug fixes.
+ *
+ *   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
+ *
+ *                  If you change this software, you should mail a .diff
+ *                  file with some description lines to emoenke@gwdg.de.
+ *                  I want to know about it.
+ *
+ *                  If you are the editor of a Linux CD, you should
+ *                  enable sbpcd.c within your boot floppy kernel and
+ *                  send me one of your CDs for free.
+ *
+ *   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.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   (for example /usr/src/linux/COPYING); if not, write to the Free
+ *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef SBPCD_ISSUE
+#define SBPCD_ISSUE 1
+#endif SBPCD_ISSUE
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[]=UTS_RELEASE;
+#endif
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif MODULE
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/major.h> 
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+#include <linux/sbpcd.h>
+
+#if !(SBPCD_ISSUE-1)
+#define MAJOR_NR MATSUSHITA_CDROM_MAJOR
+#endif
+#if !(SBPCD_ISSUE-2)
+#define MAJOR_NR MATSUSHITA_CDROM2_MAJOR /* second driver issue */
+#endif
+#if !(SBPCD_ISSUE-3)
+#define MAJOR_NR MATSUSHITA_CDROM3_MAJOR /* third driver issue */
+#endif
+#if !(SBPCD_ISSUE-4)
+#define MAJOR_NR MATSUSHITA_CDROM4_MAJOR /* fourth driver issue */
+#endif
+
+#include <linux/blk.h>
+
+#define VERSION "v3.9 Eberhard Moenkeberg <emoenke@gwdg.de>"
+
+/*==========================================================================*/
+/*
+ * provisions for more than 1 driver issues
+ * currently up to 4 drivers, expandable
+ */
+#if !(SBPCD_ISSUE-1)
+#define DO_SBPCD_REQUEST(a) do_sbpcd_request(a)
+#define SBPCD_INIT(a) sbpcd_init(a)
+#endif
+#if !(SBPCD_ISSUE-2)
+#define DO_SBPCD_REQUEST(a) do_sbpcd2_request(a)
+#define SBPCD_INIT(a) sbpcd2_init(a)
+#endif
+#if !(SBPCD_ISSUE-3)
+#define DO_SBPCD_REQUEST(a) do_sbpcd3_request(a)
+#define SBPCD_INIT(a) sbpcd3_init(a)
+#endif
+#if !(SBPCD_ISSUE-4)
+#define DO_SBPCD_REQUEST(a) do_sbpcd4_request(a)
+#define SBPCD_INIT(a) sbpcd4_init(a)
+#endif
+/*==========================================================================*/
+#if SBPCD_DIS_IRQ
+#define SBPCD_CLI cli()
+#define SBPCD_STI sti()
+#else
+#define SBPCD_CLI
+#define SBPCD_STI
+#endif SBPCD_DIS_IRQ
+/*==========================================================================*/
+/*
+ * auto-probing address list
+ * inspired by Adam J. Richter from Yggdrasil
+ *
+ * still not good enough - can cause a hang.
+ *   example: a NE 2000 ethernet card at 300 will cause a hang probing 310.
+ * if that happens, reboot and use the LILO (kernel) command line.
+ * The possibly conflicting ethernet card addresses get NOT probed 
+ * by default - to minimize the hang possibilities. 
+ *
+ * The SB Pro addresses get "mirrored" at 0x6xx and some more locations - to
+ * avoid a type error, the 0x2xx-addresses must get checked before 0x6xx.
+ *
+ * send mail to emoenke@gwdg.de if your interface card is not FULLY
+ * represented here.
+ */
+#if !(SBPCD_ISSUE-1)
+static int sbpcd[] = 
+{
+	CDROM_PORT, SBPRO, /* probe with user's setup first */
+#if DISTRIBUTION
+	0x230, 1, /* Soundblaster Pro and 16 (default) */
+	0x300, 0, /* CI-101P (default), WDH-7001C (default),
+		     Galaxy (default), Reveal (one default) */
+	0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
+	0x260, 1, /* OmniCD */
+	0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default),
+		     Longshine LCS-6853 (default) */
+	0x338, 0, /* Reveal Sound Wave 32 card model #SC600 */
+	0x340, 0, /* Mozart sound card (default), Lasermate, CI-101P */
+	0x360, 0, /* Lasermate, CI-101P */
+	0x270, 1, /* Soundblaster 16 */
+	0x670, 0, /* "sound card #9" */
+	0x690, 0, /* "sound card #9" */
+	0x338, 2, /* SPEA Media FX, Ensonic SoundScape (default) */
+	0x328, 2, /* SPEA Media FX */
+	0x348, 2, /* SPEA Media FX */
+	0x634, 0, /* some newer sound cards */
+	0x638, 0, /* some newer sound cards */
+	0x230, 1, /* some newer sound cards */
+	/* due to incomplete address decoding of the SbPro card, these must be last */
+	0x630, 0, /* "sound card #9" (default) */
+	0x650, 0, /* "sound card #9" */
+#ifdef MODULE
+	/*
+	 * some "hazardous" locations (no harm with the loadable version)
+	 * (will stop the bus if a NE2000 ethernet card resides at offset -0x10)
+	 */
+	0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
+	0x350, 0, /* Lasermate, CI-101P */
+	0x358, 2, /* SPEA Media FX */
+	0x370, 0, /* Lasermate, CI-101P */
+	0x290, 1, /* Soundblaster 16 */
+	0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
+#endif MODULE
+#endif DISTRIBUTION
+};
+#else
+static int sbpcd[] = {CDROM_PORT, SBPRO}; /* probe with user's setup only */
+#endif
+
+#define NUM_PROBE  (sizeof(sbpcd) / sizeof(int))
+
+/*==========================================================================*/
+/*
+ * the external references:
+ */
+#if !(SBPCD_ISSUE-1)
+#ifdef CONFIG_SBPCD2
+extern int sbpcd2_init(void);
+#endif
+#ifdef CONFIG_SBPCD3
+extern int sbpcd3_init(void);
+#endif
+#ifdef CONFIG_SBPCD4
+extern int sbpcd4_init(void);
+#endif
+#endif
+
+/*==========================================================================*/
+
+#define INLINE inline
+
+/*==========================================================================*/
+/*
+ * the forward references:
+ */
+static void sbp_sleep(u_int);
+static void mark_timeout_delay(u_long);
+static void mark_timeout_data(u_long);
+#if 0
+static void mark_timeout_audio(u_long);
+#endif
+static void sbp_read_cmd(void);
+static int sbp_data(void);
+static int cmd_out(void);
+static int DiskInfo(void);
+static int sbpcd_chk_disk_change(kdev_t);
+
+/*==========================================================================*/
+
+/*
+ * pattern for printk selection:
+ *
+ * (1<<DBG_INF)  necessary information
+ * (1<<DBG_BSZ)  BLOCK_SIZE trace
+ * (1<<DBG_REA)  "read" status trace
+ * (1<<DBG_CHK)  "media check" trace
+ * (1<<DBG_TIM)  datarate timer test
+ * (1<<DBG_INI)  initialization trace
+ * (1<<DBG_TOC)  tell TocEntry values
+ * (1<<DBG_IOC)  ioctl trace
+ * (1<<DBG_STA)  "ResponseStatus" trace
+ * (1<<DBG_ERR)  "cc_ReadError" trace
+ * (1<<DBG_CMD)  "cmd_out" trace
+ * (1<<DBG_WRN)  give explanation before auto-probing
+ * (1<<DBG_MUL)  multi session code test
+ * (1<<DBG_IDX)  "drive_id != 0" test code
+ * (1<<DBG_IOX)  some special information
+ * (1<<DBG_DID)  drive ID test
+ * (1<<DBG_RES)  drive reset info
+ * (1<<DBG_SPI)  SpinUp test info
+ * (1<<DBG_IOS)  ioctl trace: "subchannel"
+ * (1<<DBG_IO2)  ioctl trace: general
+ * (1<<DBG_UPC)  show UPC info
+ * (1<<DBG_XA1)  XA mode debugging
+ * (1<<DBG_LCK)  door (un)lock info
+ * (1<<DBG_SQ1)   dump SubQ frame
+ * (1<<DBG_AUD)  "read audio" debugging
+ * (1<<DBG_SEQ)  Sequoia interface configuration trace
+ * (1<<DBG_LCS)  Longshine LCS-7260 debugging trace
+ * (1<<DBG_CD2)  MKE CD200 debugging trace
+ * (1<<DBG_TEA)  TEAC CD-55A debugging trace
+ * (1<<DBG_TE2)  TEAC CD-55A debugging trace, 2nd level
+ * (1<<DBG_000)  unnecessary information
+ */
+#if DISTRIBUTION
+static int sbpcd_debug = (1<<DBG_INF);
+#else
+static int sbpcd_debug = ((1<<DBG_INF) |
+			  (1<<DBG_TOC) |
+			  (1<<DBG_MUL) |
+			  (1<<DBG_UPC));
+#endif DISTRIBUTION
+
+static int sbpcd_ioaddr = CDROM_PORT;	/* default I/O base address */
+static int sbpro_type = SBPRO;
+static unsigned char setup_done = 0;
+static int CDo_command, CDo_reset;
+static int CDo_sel_i_d, CDo_enable;
+static int CDi_info, CDi_status, CDi_data;
+static int MIXER_addr, MIXER_data;
+static struct cdrom_msf msf;
+static struct cdrom_ti ti;
+static struct cdrom_tochdr tochdr;
+static struct cdrom_tocentry tocentry;
+static struct cdrom_subchnl SC;
+static struct cdrom_volctrl volctrl;
+static struct cdrom_read_audio read_audio;
+static struct cdrom_multisession ms_info;
+
+static unsigned char msgnum=0;
+static char msgbuf[80];
+
+static const char *str_sb = "SoundBlaster";
+static const char *str_sb_l = "soundblaster";
+static const char *str_lm = "LaserMate";
+static const char *str_sp = "SPEA";
+static const char *str_sp_l = "spea";
+static const char *str_ss = "SoundScape";
+static const char *str_ss_l = "soundscape";
+const char *type;
+
+#if !(SBPCD_ISSUE-1)
+static const char *major_name="sbpcd";
+#endif
+#if !(SBPCD_ISSUE-2)
+static const char *major_name="sbpcd2";
+#endif
+#if !(SBPCD_ISSUE-3)
+static const char *major_name="sbpcd3";
+#endif
+#if !(SBPCD_ISSUE-4)
+static const char *major_name="sbpcd4";
+#endif
+
+/*==========================================================================*/
+
+#if FUTURE
+static struct wait_queue *sbp_waitq = NULL;
+#endif FUTURE
+
+/*==========================================================================*/
+#define SBP_BUFFER_FRAMES 8 /* driver's own read_ahead, data mode */
+/*==========================================================================*/
+
+static u_char family0[]="MATSHITA"; /* MKE CR-52x */
+static u_char family1[]="CR-56";    /* MKE CR-56x */
+static u_char family2[]="CD200";    /* MKE CD200 */
+static u_char familyL[]="LCS-7260"; /* Longshine LCS-7260 */
+static u_char familyT[]="CD-55";    /* TEAC CD-55A */
+
+static u_int recursion=0; /* internal testing only */
+static u_int fatal_err=0; /* internal testing only */
+static u_int response_count=0;
+static u_int flags_cmd_out;
+static u_char cmd_type=0;
+static u_char drvcmd[10];
+static u_char infobuf[20];
+static u_char xa_head_buf[CD_XA_HEAD];
+static u_char xa_tail_buf[CD_XA_TAIL];
+
+static volatile u_char busy_data=0;
+static volatile u_char busy_audio=0; /* true semaphores would be safer */
+static u_long timeout;
+static volatile u_char timed_out_delay=0;
+static volatile u_char timed_out_data=0;
+#if 0
+static volatile u_char timed_out_audio=0;
+#endif
+static u_int datarate= 1000000;
+static u_int maxtim16=16000000;
+static u_int maxtim04= 4000000;
+static u_int maxtim02= 2000000;
+static u_int maxtim_8=   30000;
+#if LONG_TIMING
+static u_int maxtim_data= 9000;
+#else
+static u_int maxtim_data= 3000;
+#endif LONG_TIMING
+#if DISTRIBUTION
+static int n_retries=3;
+#else
+static int n_retries=1;
+#endif
+/*==========================================================================*/
+
+static int ndrives=0;
+static u_char drv_pattern[NR_SBPCD]={speed_auto,speed_auto,speed_auto,speed_auto};
+static int sbpcd_blocksizes[NR_SBPCD] = {0, };
+
+/*==========================================================================*/
+/*
+ * drive space begins here (needed separate for each unit) 
+ */
+static int d=0; /* DriveStruct index: drive number */
+
+static struct {
+	char drv_id;           /* "jumpered" drive ID or -1 */
+	char drv_sel;          /* drive select lines bits */
+	
+	char drive_model[9];
+	u_char firmware_version[4];
+	char f_eject;          /* auto-eject flag: 0 or 1 */
+	u_char *sbp_buf;       /* Pointer to internal data buffer,
+				  space allocated during sbpcd_init() */
+	u_int sbp_bufsiz;      /* size of sbp_buf (# of frames) */
+	int sbp_first_frame;   /* First frame in buffer */
+	int sbp_last_frame;    /* Last frame in buffer  */
+	int sbp_read_frames;   /* Number of frames being read to buffer */
+	int sbp_current;       /* Frame being currently read */
+	
+	u_char mode;           /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */
+	u_char *aud_buf;       /* Pointer to audio data buffer,
+				  space allocated during sbpcd_init() */
+	u_int sbp_audsiz;      /* size of aud_buf (# of raw frames) */
+	u_char drv_type;
+	u_char drv_options;
+	int status_bits;
+	u_char diskstate_flags;
+	u_char sense_byte;
+	
+	u_char CD_changed;
+	char open_count;
+	u_char error_byte;
+	
+	u_char f_multisession;
+	u_int lba_multi;
+	int first_session;
+	int last_session;
+	int track_of_last_session;
+	
+	u_char audio_state;
+	u_int pos_audio_start;
+	u_int pos_audio_end;
+	char vol_chan0;
+	u_char vol_ctrl0;
+	char vol_chan1;
+	u_char vol_ctrl1;
+#if 000 /* no supported drive has it */
+	char vol_chan2;
+	u_char vol_ctrl2;
+	char vol_chan3;
+	u_char vol_ctrl3;
+#endif 000
+	u_char volume_control; /* TEAC on/off bits */
+	
+	u_char SubQ_ctl_adr;
+	u_char SubQ_trk;
+	u_char SubQ_pnt_idx;
+	u_int SubQ_run_tot;
+	u_int SubQ_run_trk;
+	u_char SubQ_whatisthis;
+	
+	u_char UPC_ctl_adr;
+	u_char UPC_buf[7];
+	
+	int frame_size;
+	int CDsize_frm;
+	
+	u_char xa_byte; /* 0x20: XA capabilities */
+	u_char n_first_track; /* binary */
+	u_char n_last_track; /* binary (not bcd), 0x01...0x63 */
+	u_int size_msf; /* time of whole CD, position of LeadOut track */
+	u_int size_blk;
+	
+	u_char TocEnt_nixbyte; /* em */
+	u_char TocEnt_ctl_adr;
+	u_char TocEnt_number;
+	u_char TocEnt_format; /* em */
+	u_int TocEnt_address;
+	u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
+	
+	struct {
+		u_char nixbyte; /* em */
+		u_char ctl_adr; /* 0x4x: data, 0x0x: audio */
+		u_char number;
+		u_char format; /* em */ /* 0x00: lba, 0x01: msf */
+		u_int address;
+	} TocBuffer[MAX_TRACKS+1]; /* last entry faked */ 
+	
+	int in_SpinUp; /* CR-52x test flag */
+	int n_bytes; /* TEAC awaited response count */
+	u_char error_state, b3, b4; /* TEAC command error state */
+	u_char f_drv_error; /* TEAC command error flag */
+	u_char speed_byte;
+	int frmsiz;
+	u_char f_XA; /* 1: XA */
+	u_char type_byte; /* 0, 1, 3 */
+	u_char mode_xb_6;
+	u_char mode_yb_7;
+	u_char mode_xb_8;
+	u_char delay;
+	
+} D_S[NR_SBPCD];
+
+/*
+ * drive space ends here (needed separate for each unit)
+ */
+/*==========================================================================*/
+#if 0
+unsigned long cli_sti; /* for saving the processor flags */
+#endif
+/*==========================================================================*/
+static struct timer_list delay_timer = { NULL, NULL, 0, 0, mark_timeout_delay};
+static struct timer_list data_timer = { NULL, NULL, 0, 0, mark_timeout_data};
+#if 0
+static struct timer_list audio_timer = { NULL, NULL, 0, 0, mark_timeout_audio};
+#endif
+/*==========================================================================*/
+/*
+ * DDI interface
+ */
+static void msg(int level, const char *fmt, ...)
+{
+	char buf[256];
+	va_list args;
+	
+	if (!(sbpcd_debug&(1<<level))) return;
+	
+	msgnum++;
+	if (msgnum>99) msgnum=0;
+	sprintf(buf, "%s-%d [%02d]:  ", major_name, d, msgnum);
+	va_start(args, fmt);
+	vsprintf(&buf[15], fmt, args);
+	va_end(args);
+	printk(buf);
+	sbp_sleep(55); /* else messages get lost */
+	return;
+}
+/*==========================================================================*/
+/*
+ * DDI interface: runtime trace bit pattern maintenance
+ */
+static int sbpcd_dbg_ioctl(unsigned long arg, int level)
+{
+	switch(arg)
+	{
+	case 0:	/* OFF */
+		sbpcd_debug = DBG_INF;
+		break;
+		
+	default:
+		if (arg>=128) sbpcd_debug &= ~(1<<(arg-128));
+		else sbpcd_debug |= (1<<arg);
+	}
+	return (arg);
+}
+/*==========================================================================*/
+static void mark_timeout_delay(u_long i)
+{
+	timed_out_delay=1;
+#if 0
+	msg(DBG_TIM,"delay timer expired.\n");
+#endif
+}
+/*==========================================================================*/
+static void mark_timeout_data(u_long i)
+{
+	timed_out_data=1;
+#if 0
+	msg(DBG_TIM,"data timer expired.\n");
+#endif
+}
+/*==========================================================================*/
+#if 0
+static void mark_timeout_audio(u_long i)
+{
+	timed_out_audio=1;
+#if 0
+	msg(DBG_TIM,"audio timer expired.\n");
+#endif
+}
+#endif
+/*==========================================================================*/
+/*
+ * Wait a little while (used for polling the drive).
+ */
+static void sbp_sleep(u_int time)
+{
+#ifndef MODULE
+	if (current == task[0]) 
+	{
+		del_timer(&delay_timer);
+		delay_timer.expires=jiffies+time;
+		timed_out_delay=0;
+		add_timer(&delay_timer);
+		while (!timed_out_delay) ;
+		return;
+	}
+#endif MODULE
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + time;
+	schedule();
+	sti();
+}
+/*==========================================================================*/
+/*
+ *  convert logical_block_address to m-s-f_number (3 bytes only)
+ */
+static INLINE void lba2msf(int lba, u_char *msf)
+{
+	lba += CD_MSF_OFFSET;
+	msf[0] = lba / (CD_SECS*CD_FRAMES);
+	lba %= CD_SECS*CD_FRAMES;
+	msf[1] = lba / CD_FRAMES;
+	msf[2] = lba % CD_FRAMES;
+}
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ *  convert msf-bin to msf-bcd
+ */
+static INLINE void bin2bcdx(u_char *p)  /* must work only up to 75 or 99 */
+{
+	*p=((*p/10)<<4)|(*p%10);
+}
+/*==========================================================================*/
+static INLINE u_int blk2msf(u_int blk)
+{
+	MSF msf;
+	u_int mm;
+	
+	msf.c[3] = 0;
+	msf.c[2] = (blk + CD_MSF_OFFSET) / (CD_SECS * CD_FRAMES);
+	mm = (blk + CD_MSF_OFFSET) % (CD_SECS * CD_FRAMES);
+	msf.c[1] = mm / CD_FRAMES;
+	msf.c[0] = mm % CD_FRAMES;
+	return (msf.n);
+}
+/*==========================================================================*/
+static INLINE u_int make16(u_char rh, u_char rl)
+{
+	return ((rh<<8)|rl);
+}
+/*==========================================================================*/
+static INLINE u_int make32(u_int rh, u_int rl)
+{
+	return ((rh<<16)|rl);
+}
+/*==========================================================================*/
+static INLINE u_char swap_nibbles(u_char i)
+{
+	return ((i<<4)|(i>>4));
+}
+/*==========================================================================*/
+static INLINE u_char byt2bcd(u_char i)
+{
+	return (((i/10)<<4)+i%10);
+}
+/*==========================================================================*/
+static INLINE u_char bcd2bin(u_char bcd)
+{
+	return ((bcd>>4)*10+(bcd&0x0F));
+}
+/*==========================================================================*/
+static INLINE int msf2blk(int msfx)
+{
+	MSF msf;
+	int i;
+	
+	msf.n=msfx;
+	i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_MSF_OFFSET;
+	if (i<0) return (0);
+	return (i);
+}
+/*==========================================================================*/
+/*
+ *  convert m-s-f_number (3 bytes only) to logical_block_address 
+ */
+static INLINE int msf2lba(u_char *msf)
+{
+	int i;
+	
+	i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_MSF_OFFSET;
+	if (i<0) return (0);
+	return (i);
+}
+/*==========================================================================*/
+/* evaluate cc_ReadError code */ 
+static int sta2err(int sta)
+{
+	if (famT_drive)
+	{
+		if (sta==0x00) return (0);
+		if (sta==0x01) return (-604); /* CRC error */
+		if (sta==0x02) return (-602); /* drive not ready */
+		if (sta==0x03) return (-607); /* unknown media */
+		if (sta==0x04) return (-612); /* general failure */
+		if (sta==0x05) return (0);
+		if (sta==0x06) return (-615); /* invalid disk change */
+		if (sta==0x0b) return (-612); /* general failure */
+		if (sta==0xff) return (-612); /* general failure */
+		return (0);
+	}
+	else
+	{
+		if (sta<=2) return (sta);
+		if (sta==0x05) return (-604); /* CRC error */
+		if (sta==0x06) return (-606); /* seek error */
+		if (sta==0x0d) return (-606); /* seek error */
+		if (sta==0x0e) return (-603); /* unknown command */
+		if (sta==0x14) return (-603); /* unknown command */
+		if (sta==0x0c) return (-611); /* read fault */
+		if (sta==0x0f) return (-611); /* read fault */
+		if (sta==0x10) return (-611); /* read fault */
+		if (sta>=0x16) return (-612); /* general failure */
+		D_S[d].CD_changed=0xFF;
+		if (sta==0x11) return (-615); /* invalid disk change (LCS: removed) */
+		if (famL_drive)
+			if (sta==0x12) return (-615); /* invalid disk change (inserted) */
+		return (-602); /* drive not ready */
+	}
+}
+/*==========================================================================*/
+static INLINE void clr_cmdbuf(void)
+{
+	int i;
+	
+	for (i=0;i<10;i++) drvcmd[i]=0;
+	cmd_type=0;
+}
+/*==========================================================================*/
+static void flush_status(void)
+{
+	int i;
+	
+#ifdef MODULE
+	sbp_sleep(15*HZ/10);
+	for (i=maxtim_data;i!=0;i--) inb(CDi_status);
+#else
+	if (current == task[0])
+		for (i=maxtim02;i!=0;i--) inb(CDi_status);
+	else 
+	{
+		sbp_sleep(15*HZ/10);
+		for (i=maxtim_data;i!=0;i--) inb(CDi_status);
+	}
+#endif MODULE
+}
+/*==========================================================================*/
+static int CDi_stat_loop(void)
+{
+	int i,j;
+	
+#ifdef MODULE
+	for(timeout = jiffies + 10*HZ, i=maxtim_data; timeout > jiffies; )
+	{
+		for ( ;i!=0;i--)
+		{
+			j=inb(CDi_status);
+			if (!(j&s_not_data_ready)) return (j);
+			if (!(j&s_not_result_ready)) return (j);
+			if (fam0L_drive) if (j&s_attention) return (j);
+		}
+		sbp_sleep(1);
+		i = 1;
+	}
+#else
+	if (current == task[0])
+		for(i=maxtim16;i!=0;i--)
+		{
+			j=inb(CDi_status);
+			if (!(j&s_not_data_ready)) return (j);
+			if (!(j&s_not_result_ready)) return (j);
+			if (fam0L_drive) if (j&s_attention) return (j);
+		}
+	else
+		for(timeout = jiffies + 10*HZ, i=maxtim_data; timeout > jiffies; )
+		{
+			for ( ;i!=0;i--)
+			{
+				j=inb(CDi_status);
+				if (!(j&s_not_data_ready)) return (j);
+				if (!(j&s_not_result_ready)) return (j);
+				if (fam0L_drive) if (j&s_attention) return (j);
+			}
+			sbp_sleep(1);
+			i = 1;
+		}
+#endif MODULE
+	msg(DBG_LCS,"CDi_stat_loop failed\n");
+	return (-1);
+}
+/*==========================================================================*/
+#if 00000
+/*==========================================================================*/
+static int tst_DataReady(void)
+{
+	int i;
+	
+	i=inb(CDi_status);
+	if (i&s_not_data_ready) return (0);
+	return (1);
+}
+/*==========================================================================*/
+static int tst_ResultReady(void)
+{
+	int i;
+	
+	i=inb(CDi_status);
+	if (i&s_not_result_ready) return (0);
+	return (1);
+}
+/*==========================================================================*/
+static int tst_Attention(void)
+{
+	int i;
+	
+	i=inb(CDi_status);
+	if (i&s_attention) return (1);
+	return (0);
+}
+/*==========================================================================*/
+#endif 00000
+/*==========================================================================*/
+static int ResponseInfo(void)
+{
+	int i,j,st=0;
+	u_long timeout;
+	
+#ifdef MODULE
+	if (0)
+#else
+	if (current == task[0])
+#endif MODULE
+		for (i=0;i<response_count;i++)
+		{
+			for (j=maxtim_8;j!=0;j--)
+			{
+				st=inb(CDi_status);
+				if (!(st&s_not_result_ready)) break;
+			}
+			if (j==0) 
+			{
+				msg(DBG_SEQ,"ResponseInfo: not_result_ready (got %d of %d bytes).\n", i, response_count);
+				break;
+			}
+			infobuf[i]=inb(CDi_info);
+		}
+	else 
+	{
+		for (i=0,timeout=jiffies+HZ;i<response_count;i++) 
+		{
+			for (j=maxtim_data; ; )
+			{
+				for ( ;j!=0;j-- )
+				{
+					st=inb(CDi_status);
+					if (!(st&s_not_result_ready)) break;
+				}
+				if ((j!=0)||(timeout<=jiffies)) break;
+				sbp_sleep(1);
+				j = 1;
+			}
+			if (timeout<=jiffies) break;
+			infobuf[i]=inb(CDi_info);
+		}
+	}
+#if 000
+	while (!(inb(CDi_status)&s_not_result_ready))
+	{
+		infobuf[i++]=inb(CDi_info);
+	}
+	j=i-response_count;
+	if (j>0) msg(DBG_INF,"ResponseInfo: got %d trailing bytes.\n",j);
+#endif 000
+	for (j=0;j<i;j++)
+		sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
+	msgbuf[j*3]=0;
+	msg(DBG_CMD,"ResponseInfo:%s (%d,%d)\n",msgbuf,response_count,i);
+	j=response_count-i;
+	if (j>0) return (-j);
+	else return (i);
+}
+/*==========================================================================*/
+static void EvaluateStatus(int st)
+{
+	D_S[d].status_bits=0;
+	if (fam1_drive) D_S[d].status_bits=st|p_success;
+	else if (fam0_drive)
+	{
+		if (st&p_caddin_old) D_S[d].status_bits |= p_door_closed|p_caddy_in;
+		if (st&p_spinning) D_S[d].status_bits |= p_spinning;
+		if (st&p_check) D_S[d].status_bits |= p_check;
+ 		if (st&p_success_old) D_S[d].status_bits |= p_success;
+ 		if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
+		if (st&p_disk_ok) D_S[d].status_bits |= p_disk_ok;
+	}
+	else if (famL_drive)
+	{
+ 		D_S[d].status_bits |= p_success;
+		if (st&p_caddin_old) D_S[d].status_bits |= p_disk_ok|p_caddy_in;
+		if (st&p_spinning) D_S[d].status_bits |= p_spinning;
+		if (st&p_check) D_S[d].status_bits |= p_check;
+		if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
+		if (st&p_lcs_door_closed) D_S[d].status_bits |= p_door_closed;
+		if (st&p_lcs_door_locked) D_S[d].status_bits |= p_door_locked;
+	}
+	else if (fam2_drive)
+	{
+ 		D_S[d].status_bits |= p_success;
+		if (st&p2_check) D_S[d].status_bits |= p1_check;
+		if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
+		if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
+		if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
+		if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
+		if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
+		if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
+		if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
+	}
+	else if (famT_drive)
+	{
+		return; /* still needs to get coded */
+ 		D_S[d].status_bits |= p_success;
+		if (st&p2_check) D_S[d].status_bits |= p1_check;
+		if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
+		if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
+		if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
+		if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
+		if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
+		if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
+		if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
+	}
+	return;
+}
+/*==========================================================================*/
+static int get_state_T(void)
+{
+	int i;
+	
+	static int cmd_out_T(void);
+
+	msg(DBG_TE2,"doing get_state_T...\n");
+	clr_cmdbuf();
+	D_S[d].n_bytes=1;
+	drvcmd[0]=CMDT_STATUS;
+	i=cmd_out_T();
+	if (i>=0) i=infobuf[0];
+	else
+	{
+		msg(DBG_TEA,"get_state_T error %d\n", i);
+		return (i);
+	}
+	if (i>=0)
+		/* 2: closed, disk in */
+		D_S[d].status_bits=p1_door_closed|p1_disk_in|p1_spinning|p1_disk_ok;
+	else if (D_S[d].error_state==6)
+		/* 3: closed, disk in, changed ("06 xx xx") */
+		D_S[d].status_bits=p1_door_closed|p1_disk_in;
+	else if ((D_S[d].error_state!=2)||(D_S[d].b3!=0x3A)||(D_S[d].b4==0x00))
+	{
+		/* 1: closed, no disk ("xx yy zz"or "02 3A 00") */
+		D_S[d].status_bits=p1_door_closed;
+		D_S[d].open_count=0;
+	}
+	else if (D_S[d].b4==0x01)
+	{
+		/* 0: open ("02 3A 01") */
+		D_S[d].status_bits=0;
+		D_S[d].open_count=0;
+	}
+	else
+	{
+		/* 1: closed, no disk ("02 3A xx") */
+		D_S[d].status_bits=p1_door_closed;
+		D_S[d].open_count=0;
+	}
+	msg(DBG_TE2,"get_state_T done (%02X)...\n", D_S[d].status_bits);
+	return (D_S[d].status_bits);
+}
+/*==========================================================================*/
+static int ResponseStatus(void)
+{
+	int i,j;
+	u_long timeout;
+	
+	msg(DBG_STA,"doing ResponseStatus...\n");
+	if (famT_drive) return (get_state_T());
+#ifdef MODULE
+	if (0)
+#else
+	if (current == task[0])
+#endif MODULE
+	{
+		if (flags_cmd_out & f_respo3) j = maxtim_8;
+		else if (flags_cmd_out&f_respo2) j=maxtim16;
+		else j=maxtim04;
+		for (;j!=0;j--)
+		{
+			i=inb(CDi_status);
+			if (!(i&s_not_result_ready)) break;
+		}
+	}
+	else
+	{
+		if (flags_cmd_out & f_respo3) timeout = jiffies;
+		else if (flags_cmd_out & f_respo2) timeout = jiffies + 16*HZ;
+		else timeout = jiffies + 4*HZ;
+		j=maxtim_8;
+		do
+		{
+			for ( ;j!=0;j--)
+			{ 
+				i=inb(CDi_status);
+				if (!(i&s_not_result_ready)) break;
+			}
+			if ((j!=0)||(timeout<jiffies)) break;
+			sbp_sleep(1);
+			j = 1;
+		}
+		while (1);
+	}
+	if (j==0) 
+	{
+		if ((flags_cmd_out & f_respo3) == 0)
+			msg(DBG_STA,"ResponseStatus: timeout.\n");
+		D_S[d].status_bits=0;
+		return (-401);
+	}
+	i=inb(CDi_info);
+	msg(DBG_STA,"ResponseStatus: response %02X.\n", i);
+	EvaluateStatus(i);
+#if 0
+	if (fam0_drive)
+#endif
+		msg(DBG_STA,"status_bits=%02X, i=%02X\n",D_S[d].status_bits,i);
+#if 1
+	return (D_S[d].status_bits);
+#else
+	return (i);
+#endif 0
+}
+/*==========================================================================*/
+static void cc_ReadStatus(void)
+{
+	int i;
+	
+	msg(DBG_STA,"giving cc_ReadStatus command\n");
+	if (famT_drive) return;
+	SBPCD_CLI;
+	if (fam0L_drive) OUT(CDo_command,CMD0_STATUS);
+	else if (fam1_drive) OUT(CDo_command,CMD1_STATUS);
+	else if (fam2_drive) OUT(CDo_command,CMD2_STATUS);
+	if (!fam0L_drive) for (i=0;i<6;i++) OUT(CDo_command,0);
+	SBPCD_STI;
+}
+/*==========================================================================*/
+static int cc_ReadError(void)
+{
+	int i;
+
+	clr_cmdbuf();
+	msg(DBG_ERR,"giving cc_ReadError command.\n");
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_READ_ERR;
+		response_count=8;
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_READ_ERR;
+		response_count=6;
+		if (famL_drive)
+			flags_cmd_out=f_putcmd;
+		else
+			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_READ_ERR;
+		response_count=6;
+		flags_cmd_out=f_putcmd;
+	}
+	else if (famT_drive)
+	{
+		response_count=5;
+		drvcmd[0]=CMDT_READ_ERR;
+	}
+	i=cmd_out();
+	D_S[d].error_byte=0;
+	msg(DBG_ERR,"cc_ReadError: cmd_out(CMDx_READ_ERR) returns %d (%02X)\n",i,i);
+	if (i<0) return (i);
+	if (fam0_drive) i=1;
+	else i=2;
+	D_S[d].error_byte=infobuf[i];
+	msg(DBG_ERR,"cc_ReadError: infobuf[%d] is %d (%02X)\n",i,D_S[d].error_byte,D_S[d].error_byte);
+	i=sta2err(infobuf[i]);
+	return (i);
+}
+/*==========================================================================*/
+static int cmd_out_T(void)
+{
+#undef CMDT_TRIES
+#define CMDT_TRIES 1000
+	
+	static int cc_DriveReset(void);
+	int i, j, l, ntries;
+	
+	D_S[d].error_state=0;
+	D_S[d].b3=0;
+	D_S[d].b4=0;
+	D_S[d].f_drv_error=0;
+	for (i=0;i<10;i++) sprintf(&msgbuf[i*3]," %02X",drvcmd[i]);
+	msgbuf[i*3]=0;
+	msg(DBG_CMD,"cmd_out_T:%s\n",msgbuf);
+
+	OUT(CDo_sel_i_d,0);
+	OUT(CDo_enable,D_S[d].drv_sel);
+	i=inb(CDi_status);
+	if (!(i&s_not_result_ready))
+	do
+	{
+		j=inb(CDi_info);
+		i=inb(CDi_status);
+		sbp_sleep(0);
+		msg(DBG_TEA,"cmd_out_T: spurious !s_not_result_ready. (%02X)\n", j);
+	}
+	while (!(i&s_not_result_ready));
+	cli();
+	for (i=0;i<10;i++) OUT(CDo_command,drvcmd[i]);
+	sti();
+	for (ntries=CMDT_TRIES;ntries>0;ntries--)
+	{
+		if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ);
+#if 1
+		OUT(CDo_sel_i_d,0);
+#endif
+		i=inb(CDi_status);
+		if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */
+		{
+			OUT(CDo_sel_i_d,1);
+			if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
+			if (drvcmd[0]==CMDT_DISKINFO)
+			{
+				l=0;
+				do
+				{
+					infobuf[l++]=inb(CDi_data);
+					i=inb(CDi_status);
+				}
+				while (!(i&s_not_data_ready));
+				for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
+				msgbuf[j*3]=0;
+				msg(DBG_CMD,"cmd_out_T data response:%s\n", msgbuf);
+			}
+			else
+			{
+				msg(DBG_TEA,"cmd_out_T: data response with cmd_%02X !!!!!!!!!!!!!!!!!!!!\n", drvcmd[0]);
+				j=0;
+				do
+				{
+					i=inb(CDi_data);
+					j++;
+					i=inb(CDi_status);
+				}
+				while (!(i&s_not_data_ready));
+				msg(DBG_TEA,"cmd_out_T: data response: discarded %d bytes.\n", j);
+				fatal_err++;
+			}
+		}
+		i=inb(CDi_status);
+		if (!(i&s_not_result_ready))
+		{
+			OUT(CDo_sel_i_d,0);
+			l=0;
+			do
+			{
+				infobuf[l++]=inb(CDi_info);
+				i=inb(CDi_status);
+			}
+			while (!(i&s_not_result_ready));
+			for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
+			msgbuf[j*3]=0;
+			msg(DBG_CMD,"cmd_out_T info response:%s\n", msgbuf);
+			if (infobuf[0]!=0x02) return (l); /* info length */
+			do
+			{
+				++recursion;
+				if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (%02X): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", drvcmd[0], recursion);
+				clr_cmdbuf();
+				drvcmd[0]=CMDT_READ_ERR;
+				j=cmd_out_T(); /* !!! recursive here !!! */
+				--recursion;
+				sbp_sleep(1);
+			}
+			while (j<0);
+			D_S[d].error_state=infobuf[2];
+			D_S[d].b3=infobuf[3];
+			D_S[d].b4=infobuf[4];
+			if (D_S[d].f_drv_error)
+			{
+				D_S[d].f_drv_error=0;
+				cc_DriveReset();
+				D_S[d].error_state=2;
+			}
+			return (-D_S[d].error_state-400);
+		}
+		if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
+		sbp_sleep(HZ/10);
+		if (ntries>(CMDT_TRIES-50)) continue;
+		msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1);
+	}
+	D_S[d].f_drv_error=1;
+	cc_DriveReset();
+	D_S[d].error_state=2;
+	return (-99);
+}
+/*==========================================================================*/
+static int cmd_out(void)
+{
+	int i=0;
+	
+	if (famT_drive) return(cmd_out_T());
+	
+	if (flags_cmd_out&f_putcmd)
+	{ 
+		for (i=0;i<7;i++)
+			sprintf(&msgbuf[i*3], " %02X", drvcmd[i]);
+		msgbuf[i*3]=0;
+		msg(DBG_CMD,"cmd_out:%s\n", msgbuf);
+		cli();
+		for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+		sti();
+	}
+	if (response_count!=0)
+	{
+		if (cmd_type!=0)
+		{
+			if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+			msg(DBG_INF,"misleaded to try ResponseData.\n");
+			if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+			return (-22);
+		}
+		else i=ResponseInfo();
+		if (i<0) return (i);
+	}
+	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to CDi_stat_loop.\n");
+	if (flags_cmd_out&f_lopsta)
+	{
+		i=CDi_stat_loop();
+		if ((i<0)||!(i&s_attention)) return (-8);
+	}
+	if (!(flags_cmd_out&f_getsta)) goto LOC_229;
+	
+ LOC_228:
+	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadStatus.\n");
+	cc_ReadStatus();
+	
+ LOC_229:
+	if (flags_cmd_out&f_ResponseStatus) 
+	{
+		if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to ResponseStatus.\n");
+		i=ResponseStatus();
+		/* builds status_bits, returns orig. status or p_busy_new */
+		if (i<0) return (i);
+		if (flags_cmd_out&(f_bit1|f_wait_if_busy))
+		{
+			if (!st_check)
+			{
+				if ((flags_cmd_out&f_bit1)&&(i&p_success)) goto LOC_232;
+				if ((!(flags_cmd_out&f_wait_if_busy))||(!st_busy)) goto LOC_228;
+			}
+		}
+	}
+ LOC_232:
+	if (!(flags_cmd_out&f_obey_p_check)) return (0);
+	if (!st_check) return (0);
+	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadError.\n");
+	i=cc_ReadError();
+	if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cmd_out OK.\n");
+	msg(DBG_000,"cmd_out: cc_ReadError=%d\n", i);
+	return (i);
+}
+/*==========================================================================*/
+static int cc_Seek(u_int pos, char f_blk_msf)
+{
+	int i;
+	
+  clr_cmdbuf();
+	if (f_blk_msf>1) return (-3);
+	if (fam0_drive)
+	{
+		drvcmd[0]=CMD0_SEEK;
+		if (f_blk_msf==1) pos=msf2blk(pos);
+		drvcmd[2]=(pos>>16)&0x00FF;
+		drvcmd[3]=(pos>>8)&0x00FF;
+		drvcmd[4]=pos&0x00FF;
+		flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
+			f_ResponseStatus | f_obey_p_check | f_bit1;
+	}
+	else if (fam1L_drive)
+	{
+		drvcmd[0]=CMD1_SEEK; /* same as CMD1_ and CMDL_ */
+		if (f_blk_msf==0) pos=blk2msf(pos);
+		drvcmd[1]=(pos>>16)&0x00FF;
+		drvcmd[2]=(pos>>8)&0x00FF;
+		drvcmd[3]=pos&0x00FF;
+		if (famL_drive)
+			flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+		else
+			flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_SEEK;
+		if (f_blk_msf==0) pos=blk2msf(pos);
+		drvcmd[2]=(pos>>24)&0x00FF;
+		drvcmd[3]=(pos>>16)&0x00FF;
+		drvcmd[4]=(pos>>8)&0x00FF;
+		drvcmd[5]=pos&0x00FF;
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (famT_drive)
+	{
+		drvcmd[0]=CMDT_SEEK;
+		if (f_blk_msf==1) pos=msf2blk(pos);
+		drvcmd[2]=(pos>>24)&0x00FF;
+		drvcmd[3]=(pos>>16)&0x00FF;
+		drvcmd[4]=(pos>>8)&0x00FF;
+		drvcmd[5]=pos&0x00FF;
+		D_S[d].n_bytes=1;
+	}
+	response_count=0;
+	i=cmd_out();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_SpinUp(void)
+{
+	int i;
+	
+	msg(DBG_SPI,"SpinUp.\n");
+	D_S[d].in_SpinUp = 1;
+	clr_cmdbuf();
+	if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_SPINUP;
+		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+			f_ResponseStatus|f_obey_p_check|f_bit1;
+	}
+	else if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_SPINUP;
+		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_TRAY_CTL;
+		drvcmd[4]=0x01; /* "spinup" */
+		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (famT_drive)
+	{
+		drvcmd[0]=CMDT_TRAY_CTL;
+		drvcmd[4]=0x03; /* "insert", it hopefully spins the drive up */
+	}
+	response_count=0;
+	i=cmd_out();
+	D_S[d].in_SpinUp = 0;
+	return (i);
+}
+/*==========================================================================*/
+static int cc_SpinDown(void)
+{
+	int i;
+	
+	if (fam0_drive) return (0);
+	clr_cmdbuf();
+	response_count=0;
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_SPINDOWN;
+		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_TRAY_CTL;
+		drvcmd[4]=0x02; /* "eject" */
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (famL_drive)
+	{
+		drvcmd[0]=CMDL_SPINDOWN;
+		drvcmd[1]=1;
+		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+	}
+	else if (famT_drive)
+	{
+		drvcmd[0]=CMDT_TRAY_CTL;
+		drvcmd[4]=0x02; /* "eject" */
+	}
+	i=cmd_out();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_get_mode_T(void)
+{
+	int i;
+	
+	clr_cmdbuf();
+	response_count=10;
+	drvcmd[0]=CMDT_GETMODE;
+	drvcmd[4]=response_count;
+	i=cmd_out_T();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_set_mode_T(void)
+{
+	int i;
+	
+	clr_cmdbuf();
+	response_count=1;
+	drvcmd[0]=CMDT_SETMODE;
+	drvcmd[1]=D_S[d].speed_byte;
+	drvcmd[2]=D_S[d].frmsiz>>8;
+	drvcmd[3]=D_S[d].frmsiz&0x0FF;
+	drvcmd[4]=D_S[d].f_XA; /* 1: XA */
+	drvcmd[5]=D_S[d].type_byte; /* 0, 1, 3 */
+	drvcmd[6]=D_S[d].mode_xb_6;
+	drvcmd[7]=D_S[d].mode_yb_7|D_S[d].volume_control;
+	drvcmd[8]=D_S[d].mode_xb_8;
+	drvcmd[9]=D_S[d].delay;
+	i=cmd_out_T();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_prep_mode_T(void)
+{
+	int i, j;
+	
+	i=cc_get_mode_T();
+	if (i<0) return (i);
+	for (i=0;i<10;i++)
+		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+	msgbuf[i*3]=0;
+	msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
+	D_S[d].speed_byte=0x02; /* 0x02: auto quad, 0x82: quad, 0x81: double, 0x80: single */
+	D_S[d].frmsiz=make16(infobuf[2],infobuf[3]);
+	D_S[d].f_XA=infobuf[4];
+	if (D_S[d].f_XA==0) D_S[d].type_byte=0;
+	else D_S[d].type_byte=1;
+	D_S[d].mode_xb_6=infobuf[6];
+	D_S[d].mode_yb_7=1;
+	D_S[d].mode_xb_8=infobuf[8];
+	D_S[d].delay=0; /* 0, 1, 2, 3 */
+	j=cc_set_mode_T();
+	i=cc_get_mode_T();
+	for (i=0;i<10;i++)
+		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+	msgbuf[i*3]=0;
+	msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
+	return (j);
+}
+/*==========================================================================*/
+static int cc_SetSpeed(u_char speed, u_char x1, u_char x2)
+{
+	int i;
+	
+	if (fam0L_drive) return (-3);
+	clr_cmdbuf();
+	response_count=0;
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_SETMODE;
+		drvcmd[1]=0x03;
+		drvcmd[2]=speed;
+		drvcmd[3]=x1;
+		drvcmd[4]=x2;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_SETSPEED;
+		if (speed&speed_auto)
+		{
+			drvcmd[2]=0xFF;
+			drvcmd[3]=0xFF;
+		}
+		else
+		{
+			drvcmd[2]=0;
+			drvcmd[3]=150;
+		}
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (famT_drive)
+	{
+		return (0);
+	}
+	i=cmd_out();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_SetVolume(void)
+{
+	int i;
+	u_char channel0,channel1,volume0,volume1;
+	u_char control0,value0,control1,value1;
+	
+	D_S[d].diskstate_flags &= ~volume_bit;
+	clr_cmdbuf();
+	channel0=D_S[d].vol_chan0;
+	volume0=D_S[d].vol_ctrl0;
+	channel1=control1=D_S[d].vol_chan1;
+	volume1=value1=D_S[d].vol_ctrl1;
+	control0=value0=0;
+	
+	if (((D_S[d].drv_options&audio_mono)!=0)&&(D_S[d].drv_type>=drv_211))
+	{
+		if ((volume0!=0)&&(volume1==0))
+		{
+			volume1=volume0;
+			channel1=channel0;
+		}
+		else if ((volume0==0)&&(volume1!=0))
+		{
+			volume0=volume1;
+			channel0=channel1;
+		}
+	}
+	if (channel0>1)
+	{
+		channel0=0;
+		volume0=0;
+	}
+	if (channel1>1)
+	{
+		channel1=1;
+		volume1=0;
+	}
+	
+	if (fam1_drive)
+	{
+		control0=channel0+1;
+		control1=channel1+1;
+		value0=(volume0>volume1)?volume0:volume1;
+		value1=value0;
+		if (volume0==0) control0=0;
+		if (volume1==0) control1=0;
+		drvcmd[0]=CMD1_SETMODE;
+		drvcmd[1]=0x05;
+		drvcmd[3]=control0;
+		drvcmd[4]=value0;
+		drvcmd[5]=control1;
+		drvcmd[6]=value1;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		control0=channel0+1;
+		control1=channel1+1;
+		value0=(volume0>volume1)?volume0:volume1;
+		value1=value0;
+		if (volume0==0) control0=0;
+		if (volume1==0) control1=0;
+		drvcmd[0]=CMD2_SETMODE;
+		drvcmd[1]=0x0E;
+		drvcmd[3]=control0;
+		drvcmd[4]=value0;
+		drvcmd[5]=control1;
+		drvcmd[6]=value1;
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (famL_drive)
+	{
+		if ((volume0==0)||(channel0!=0)) control0 |= 0x80;
+		if ((volume1==0)||(channel1!=1)) control0 |= 0x40;
+		if (volume0|volume1) value0=0x80;
+		drvcmd[0]=CMDL_SETMODE;
+		drvcmd[1]=0x03;
+		drvcmd[4]=control0;
+		drvcmd[5]=value0;
+		flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+	}
+	else if (fam0_drive) /* different firmware levels */
+	{
+		if (D_S[d].drv_type>=drv_300)
+		{
+			control0=volume0&0xFC;
+			value0=volume1&0xFC;
+			if ((volume0!=0)&&(volume0<4)) control0 |= 0x04;
+			if ((volume1!=0)&&(volume1<4)) value0 |= 0x04;
+			if (channel0!=0) control0 |= 0x01;
+			if (channel1==1) value0 |= 0x01;
+		}
+		else
+		{
+			value0=(volume0>volume1)?volume0:volume1;
+			if (D_S[d].drv_type<drv_211)
+			{
+				if (channel0!=0)
+				{
+					i=channel1;
+					channel1=channel0;
+					channel0=i;
+					i=volume1;
+					volume1=volume0;
+					volume0=i;
+				}
+				if (channel0==channel1)
+				{
+					if (channel0==0)
+					{
+						channel1=1;
+						volume1=0;
+						volume0=value0;
+					}
+					else
+					{
+						channel0=0;
+						volume0=0;
+						volume1=value0;
+					}
+				}
+			}
+			
+			if ((volume0!=0)&&(volume1!=0))
+			{
+				if (volume0==0xFF) volume1=0xFF;
+				else if (volume1==0xFF) volume0=0xFF;
+			}
+			else if (D_S[d].drv_type<drv_201) volume0=volume1=value0;
+			
+			if (D_S[d].drv_type>=drv_201)
+			{
+				if (volume0==0) control0 |= 0x80;
+				if (volume1==0) control0 |= 0x40;
+			}
+			if (D_S[d].drv_type>=drv_211)
+			{
+				if (channel0!=0) control0 |= 0x20;
+				if (channel1!=1) control0 |= 0x10;
+			}
+		}
+		drvcmd[0]=CMD0_SETMODE;
+		drvcmd[1]=0x83;
+		drvcmd[4]=control0;
+		drvcmd[5]=value0;
+		flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (famT_drive)
+	{
+		D_S[d].volume_control=0;
+		if (!volume0) D_S[d].volume_control|=0x10;
+		if (!volume1) D_S[d].volume_control|=0x20;
+		i=cc_prep_mode_T();
+		if (i<0) return (i);
+	}
+	if (!famT_drive)
+	{
+		response_count=0;
+		i=cmd_out();
+		if (i<0) return (i);
+	}
+	D_S[d].diskstate_flags |= volume_bit;
+	return (0);
+}
+/*==========================================================================*/
+static int GetStatus(void)
+{
+	int i;
+	
+	if (famT_drive) return (0);
+	flags_cmd_out=f_getsta|f_ResponseStatus|f_obey_p_check;
+	response_count=0;
+	cmd_type=0;
+	i=cmd_out();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_DriveReset(void)
+{
+	int i;
+	
+	msg(DBG_RES,"cc_DriveReset called.\n");
+	clr_cmdbuf();
+	response_count=0;
+	if (fam0L_drive) OUT(CDo_reset,0x00);
+	else if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_RESET;
+		flags_cmd_out=f_putcmd;
+		i=cmd_out();
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_RESET;
+		flags_cmd_out=f_putcmd;
+		i=cmd_out();
+		OUT(CDo_reset,0x00);
+	}
+	else if (famT_drive)
+	{
+		OUT(CDo_sel_i_d,0);
+		OUT(CDo_enable,D_S[d].drv_sel);
+		OUT(CDo_command,CMDT_RESET);
+		for (i=1;i<10;i++) OUT(CDo_command,0);
+	}
+	if (fam0L_drive) sbp_sleep(5*HZ); /* wait 5 seconds */
+	else sbp_sleep(1*HZ); /* wait a second */
+#if 1
+	if (famT_drive)
+	{
+		msg(DBG_TEA, "================CMDT_RESET given=================.\n");
+		sbp_sleep(3*HZ);
+	}
+#endif 1
+	flush_status();
+	i=GetStatus();
+	if (i<0) return i;
+	if (!famT_drive)
+		if (D_S[d].error_byte!=aud_12) return -501;
+	return (0);
+}
+/*==========================================================================*/
+static int SetSpeed(void)
+{
+	int i, speed;
+	
+	if (!(D_S[d].drv_options&(speed_auto|speed_300|speed_150))) return (0);
+	speed=speed_auto;
+	if (!(D_S[d].drv_options&speed_auto))
+	{
+		speed |= speed_300;
+		if (!(D_S[d].drv_options&speed_300)) speed=0;
+	}
+	i=cc_SetSpeed(speed,0,0);
+	return (i);
+}
+/*==========================================================================*/
+static int DriveReset(void)
+{
+	int i;
+	
+	i=cc_DriveReset();
+	if (i<0) return (-22);
+	do
+	{
+		i=GetStatus();
+		if ((i<0)&&(i!=-615)) return (-2); /* i!=-615 is from sta2err */
+		if (!st_caddy_in) break;
+		sbp_sleep(1);
+	}
+	while (!st_diskok);
+#if 000
+	D_S[d].CD_changed=1;
+#endif
+	if ((st_door_closed) && (st_caddy_in))
+	{
+		i=DiskInfo();
+		if (i<0) return (-23);
+	}
+	return (0);
+}
+/*==========================================================================*/
+static int cc_PlayAudio(int pos_audio_start,int pos_audio_end)
+{
+	int i, j, n;
+	
+	if (D_S[d].audio_state==audio_playing) return (-EINVAL);
+	clr_cmdbuf();
+	response_count=0;
+	if (famL_drive)
+	{
+		drvcmd[0]=CMDL_PLAY;
+		i=msf2blk(pos_audio_start);
+		n=msf2blk(pos_audio_end)+1-i;
+		drvcmd[1]=(i>>16)&0x00FF;
+		drvcmd[2]=(i>>8)&0x00FF;
+		drvcmd[3]=i&0x00FF;
+		drvcmd[4]=(n>>16)&0x00FF;
+		drvcmd[5]=(n>>8)&0x00FF;
+		drvcmd[6]=n&0x00FF;
+		flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
+			f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
+	}
+	else
+	{
+		j=1;
+		if (fam1_drive)
+		{
+			drvcmd[0]=CMD1_PLAY_MSF;
+			flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus |
+				f_obey_p_check | f_wait_if_busy;
+		}
+		else if (fam2_drive)
+		{
+			drvcmd[0]=CMD2_PLAY_MSF;
+			flags_cmd_out = f_putcmd | f_ResponseStatus;
+		}
+		else if (famT_drive)
+		{
+			drvcmd[0]=CMDT_PLAY_MSF;
+			j=3;
+			response_count=1;
+		}
+		else if (fam0_drive)
+		{
+			drvcmd[0]=CMD0_PLAY_MSF;
+			flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
+				f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
+		}
+		drvcmd[j]=(pos_audio_start>>16)&0x00FF;
+		drvcmd[j+1]=(pos_audio_start>>8)&0x00FF;
+		drvcmd[j+2]=pos_audio_start&0x00FF;
+		drvcmd[j+3]=(pos_audio_end>>16)&0x00FF;
+		drvcmd[j+4]=(pos_audio_end>>8)&0x00FF;
+		drvcmd[j+5]=pos_audio_end&0x00FF;
+	}
+	i=cmd_out();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_Pause_Resume(int pau_res)
+{
+	int i;
+	
+	clr_cmdbuf();
+	response_count=0;
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_PAU_RES;
+		if (pau_res!=1) drvcmd[1]=0x80;
+		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_PAU_RES;
+		if (pau_res!=1) drvcmd[2]=0x01;
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_PAU_RES;
+		if (pau_res!=1) drvcmd[1]=0x80;
+		if (famL_drive)
+			flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+				f_obey_p_check|f_bit1;
+		else
+			flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+				f_obey_p_check;
+	}
+	else if (famT_drive)
+	{
+		if (pau_res==3)	return (cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end));
+		else if (pau_res==1) drvcmd[0]=CMDT_PAUSE;
+		else return (-56);
+	}
+	i=cmd_out();
+	return (i);
+}
+/*==========================================================================*/
+static int cc_LockDoor(char lock)
+{
+	int i;
+	
+	if (fam0_drive) return (0);
+	msg(DBG_LCK,"cc_LockDoor: %d (drive %d)\n", lock, d);
+	msg(DBG_LCS,"p_door_locked bit %d before\n", st_door_locked);
+	clr_cmdbuf();
+	response_count=0;
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_LOCK_CTL;
+		if (lock==1) drvcmd[1]=0x01;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_LOCK_CTL;
+		if (lock==1) drvcmd[4]=0x01;
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (famL_drive)
+	{
+		drvcmd[0]=CMDL_LOCK_CTL;
+		if (lock==1) drvcmd[1]=0x01;
+		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+	}
+	else if (famT_drive)
+	{
+		drvcmd[0]=CMDT_LOCK_CTL;
+		if (lock==1) drvcmd[4]=0x01;
+	}
+	i=cmd_out();
+	msg(DBG_LCS,"p_door_locked bit %d after\n", st_door_locked);
+	return (i);
+}
+/*==========================================================================*/
+/*==========================================================================*/
+static int UnLockDoor(void)
+{
+	int i,j;
+	
+	j=20;
+	do
+	{
+		i=cc_LockDoor(0);
+		--j;
+		sbp_sleep(1);
+	}
+	while ((i<0)&&(j));
+	if (i<0)
+	{
+		cc_DriveReset();
+		return -84;
+	}
+	return (0);
+}
+/*==========================================================================*/
+static int LockDoor(void)
+{
+	int i,j;
+	
+	j=20;
+	do
+	{
+		i=cc_LockDoor(1);
+		--j;
+		sbp_sleep(1);
+	}
+	while ((i<0)&&(j));
+	if (j==0)
+	{		
+		cc_DriveReset();
+		j=20;
+		do
+		{
+			i=cc_LockDoor(1);
+			--j;
+			sbp_sleep(1);
+		}
+		while ((i<0)&&(j));
+	}
+	return (i);
+}
+/*==========================================================================*/
+static int cc_CloseTray(void)
+{
+	int i;
+	
+	if (fam0_drive) return (0);
+	msg(DBG_LCK,"cc_CloseTray (drive %d)\n", d);
+	msg(DBG_LCS,"p_door_closed bit %d before\n", st_door_closed);
+	
+	clr_cmdbuf();
+	response_count=0;
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_TRAY_CTL;
+		flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_TRAY_CTL;
+		drvcmd[1]=0x01;
+		drvcmd[4]=0x03; /* "insert" */
+		flags_cmd_out=f_putcmd|f_ResponseStatus;
+	}
+	else if (famL_drive)
+	{
+		drvcmd[0]=CMDL_TRAY_CTL;
+		flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+			f_ResponseStatus|f_obey_p_check|f_bit1;
+	}
+	else if (famT_drive)
+	{
+		drvcmd[0]=CMDT_TRAY_CTL;
+		drvcmd[4]=0x03; /* "insert" */
+	}
+	i=cmd_out();
+	msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed);
+	return (i);
+}
+/*==========================================================================*/
+static int cc_ReadSubQ(void)
+{
+	int i,j;
+
+	D_S[d].diskstate_flags &= ~subq_bit;
+	for (j=255;j>0;j--)
+	{
+		clr_cmdbuf();
+		if (fam1_drive)
+		{
+			drvcmd[0]=CMD1_READSUBQ;
+			flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+			response_count=11;
+		}
+		else if (fam2_drive)
+		{
+			drvcmd[0]=CMD2_READSUBQ;
+			drvcmd[1]=0x02;
+			drvcmd[3]=0x01;
+			flags_cmd_out=f_putcmd;
+			response_count=10;
+		}
+		else if (fam0L_drive)
+		{
+			drvcmd[0]=CMD0_READSUBQ;
+			drvcmd[1]=0x02;
+			if (famL_drive)
+				flags_cmd_out=f_putcmd;
+			else
+				flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+			response_count=13;
+		}
+		else if (famT_drive)
+		{
+			response_count=12;
+			drvcmd[0]=CMDT_READSUBQ;
+			drvcmd[1]=0x02;
+			drvcmd[2]=0x40;
+			drvcmd[3]=0x01;
+			drvcmd[8]=response_count;
+		}
+		i=cmd_out();
+		if (i<0) return (i);
+		for (i=0;i<response_count;i++)
+		{
+			sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+			msgbuf[i*3]=0;
+			msg(DBG_SQ1,"cc_ReadSubQ:%s\n", msgbuf);
+		}
+		if (famT_drive) break;
+		if (infobuf[0]!=0) break;
+		if ((!st_spinning) || (j==1))
+		{
+			D_S[d].SubQ_ctl_adr=D_S[d].SubQ_trk=D_S[d].SubQ_pnt_idx=D_S[d].SubQ_whatisthis=0;
+			D_S[d].SubQ_run_tot=D_S[d].SubQ_run_trk=0;
+			return (0);
+		}
+	}
+	if (famT_drive) D_S[d].SubQ_ctl_adr=infobuf[1];
+	else D_S[d].SubQ_ctl_adr=swap_nibbles(infobuf[1]);
+	D_S[d].SubQ_trk=byt2bcd(infobuf[2]);
+	D_S[d].SubQ_pnt_idx=byt2bcd(infobuf[3]);
+	if (fam0L_drive) i=5;
+	else if (fam12_drive) i=4;
+	else if (famT_drive) i=8;
+	D_S[d].SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
+	i=7;
+	if (fam0L_drive) i=9;
+	else if (fam12_drive) i=7;
+	else if (famT_drive) i=4;
+	D_S[d].SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
+	D_S[d].SubQ_whatisthis=infobuf[i+3];
+	D_S[d].diskstate_flags |= subq_bit;
+	return (0);
+}
+/*==========================================================================*/
+static int cc_ModeSense(void)
+{
+	int i;
+	
+	if (fam2_drive) return (0);
+	D_S[d].diskstate_flags &= ~frame_size_bit;
+	clr_cmdbuf();
+	if (fam1_drive)
+	{
+		response_count=5;
+		drvcmd[0]=CMD1_GETMODE;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam0L_drive)
+	{
+		response_count=2;
+		drvcmd[0]=CMD0_GETMODE;
+		if (famL_drive) flags_cmd_out=f_putcmd;
+		else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (famT_drive)
+	{
+		response_count=10;
+		drvcmd[0]=CMDT_GETMODE;
+		drvcmd[4]=response_count;
+	}
+	i=cmd_out();
+	if (i<0) return (i);
+	i=0;
+	D_S[d].sense_byte=0;
+	if (fam1_drive) D_S[d].sense_byte=infobuf[i++];
+	else if (famT_drive)
+	{
+		if (infobuf[4]==0x01) D_S[d].xa_byte=0x20;
+		else D_S[d].xa_byte=0;
+		i=2;
+	}
+	D_S[d].frame_size=make16(infobuf[i],infobuf[i+1]);
+	for (i=0;i<response_count;i++)
+		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+	msgbuf[i*3]=0;
+	msg(DBG_XA1,"cc_ModeSense:%s\n", msgbuf);
+	
+	D_S[d].diskstate_flags |= frame_size_bit;
+	return (0);
+}
+/*==========================================================================*/
+/*==========================================================================*/
+static int cc_ModeSelect(int framesize)
+{
+	int i;
+	
+	if (fam2_drive) return (0);
+	D_S[d].diskstate_flags &= ~frame_size_bit;
+	clr_cmdbuf();
+	D_S[d].frame_size=framesize;
+	if (framesize==CD_FRAMESIZE_RAW) D_S[d].sense_byte=0x82;
+	else D_S[d].sense_byte=0x00;
+	
+	msg(DBG_XA1,"cc_ModeSelect: %02X %04X\n",
+	    D_S[d].sense_byte, D_S[d].frame_size);
+	
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_SETMODE;
+		drvcmd[1]=0x00;
+		drvcmd[2]=D_S[d].sense_byte;
+		drvcmd[3]=(D_S[d].frame_size>>8)&0xFF;
+		drvcmd[4]=D_S[d].frame_size&0xFF;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_SETMODE;
+		drvcmd[1]=0x00;
+		drvcmd[2]=(D_S[d].frame_size>>8)&0xFF;
+		drvcmd[3]=D_S[d].frame_size&0xFF;
+		drvcmd[4]=0x00;
+		if(famL_drive)
+			flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check;
+		else
+			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (famT_drive)
+	{
+		return (-1);
+	}
+	response_count=0;
+	i=cmd_out();
+	if (i<0) return (i);
+	D_S[d].diskstate_flags |= frame_size_bit;
+	return (0);
+}
+/*==========================================================================*/
+static int cc_GetVolume(void)
+{
+	int i;
+	u_char switches;
+	u_char chan0=0;
+	u_char vol0=0;
+	u_char chan1=1;
+	u_char vol1=0;
+	
+	D_S[d].diskstate_flags &= ~volume_bit;
+	clr_cmdbuf();
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_GETMODE;
+		drvcmd[1]=0x05;
+		response_count=5;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_GETMODE;
+		drvcmd[1]=0x0E;
+		response_count=5;
+		flags_cmd_out=f_putcmd;
+	}
+	else if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_GETMODE;
+		drvcmd[1]=0x03;
+		response_count=2;
+		if(famL_drive)
+			flags_cmd_out=f_putcmd;
+		else
+			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (famT_drive)
+	{
+		i=cc_get_mode_T();
+		if (i<0) return (i);
+	}
+	if (!famT_drive)
+	{
+		i=cmd_out();
+		if (i<0) return (i);
+	}
+	if (fam1_drive)
+	{
+		chan0=infobuf[1]&0x0F;
+		vol0=infobuf[2];
+		chan1=infobuf[3]&0x0F;
+		vol1=infobuf[4];
+		if (chan0==0)
+		{
+			chan0=1;
+			vol0=0;
+		}
+		if (chan1==0)
+		{
+			chan1=2;
+			vol1=0;
+		}
+		chan0 >>= 1;
+		chan1 >>= 1;
+	}
+	else if (fam2_drive)
+	{
+		chan0=infobuf[1];
+		vol0=infobuf[2];
+		chan1=infobuf[3];
+		vol1=infobuf[4];
+	}
+	else if (famL_drive)
+	{
+		chan0=0;
+		chan1=1;
+		vol0=vol1=infobuf[1];
+		switches=infobuf[0];
+		if ((switches&0x80)!=0) chan0=1;
+		if ((switches&0x40)!=0) chan1=0;
+	}
+	else if (fam0_drive) /* different firmware levels */
+	{
+		chan0=0;
+		chan1=1;
+		vol0=vol1=infobuf[1];
+		if (D_S[d].drv_type>=drv_201)
+		{
+			if (D_S[d].drv_type<drv_300)
+			{
+				switches=infobuf[0];
+				if ((switches&0x80)!=0) vol0=0;
+				if ((switches&0x40)!=0) vol1=0;
+				if (D_S[d].drv_type>=drv_211)
+				{
+					if ((switches&0x20)!=0) chan0=1;
+					if ((switches&0x10)!=0) chan1=0;
+				}
+			}
+			else
+			{
+				vol0=infobuf[0];
+				if ((vol0&0x01)!=0) chan0=1;
+				if ((vol1&0x01)==0) chan1=0;
+				vol0 &= 0xFC;
+				vol1 &= 0xFC;
+				if (vol0!=0) vol0 += 3;
+				if (vol1!=0) vol1 += 3;
+			}
+		}
+	}
+	else if (famT_drive)
+	{
+		D_S[d].volume_control=infobuf[7];
+		chan0=0;
+		chan1=1;
+		if (D_S[d].volume_control&0x10) vol0=0;
+		else vol0=0xff;
+		if (D_S[d].volume_control&0x20) vol1=0;
+		else vol1=0xff;
+	}
+	D_S[d].vol_chan0=chan0;
+	D_S[d].vol_ctrl0=vol0;
+	D_S[d].vol_chan1=chan1;
+	D_S[d].vol_ctrl1=vol1;
+#if 000
+	D_S[d].vol_chan2=2;
+	D_S[d].vol_ctrl2=0xFF;
+	D_S[d].vol_chan3=3;
+	D_S[d].vol_ctrl3=0xFF;
+#endif 000
+	D_S[d].diskstate_flags |= volume_bit;
+	return (0);
+}
+/*==========================================================================*/
+static int cc_ReadCapacity(void)
+{
+	int i, j;
+	
+	if (famL_drive) return (0); /* some firmware lacks this command */
+	if (famT_drive) return (0); /* done with cc_ReadTocDescr() */
+	D_S[d].diskstate_flags &= ~cd_size_bit;
+	for (j=3;j>0;j--)
+	{
+		clr_cmdbuf();
+		if (fam1_drive)
+		{
+			drvcmd[0]=CMD1_CAPACITY;
+			response_count=5;
+			flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+		}
+		else if (fam2_drive)
+		{
+			drvcmd[0]=CMD2_CAPACITY;
+			response_count=8;
+			flags_cmd_out=f_putcmd;
+		}
+		else if (fam0_drive)
+		{
+			drvcmd[0]=CMD0_CAPACITY;
+			response_count=5;
+			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+		}
+		i=cmd_out();
+		if (i>=0) break;
+		msg(DBG_000,"cc_ReadCapacity: cmd_out: err %d\n", i);
+		cc_ReadError();
+	}
+	if (j==0) return (i);
+	if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_MSF_OFFSET;
+	else if (fam0_drive) D_S[d].CDsize_frm=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
+	else if (fam2_drive) D_S[d].CDsize_frm=make32(make16(infobuf[0],infobuf[1]),make16(infobuf[2],infobuf[3]));
+	D_S[d].diskstate_flags |= cd_size_bit;
+	msg(DBG_000,"cc_ReadCapacity: %d frames.\n", D_S[d].CDsize_frm);
+	return (0);
+}
+/*==========================================================================*/
+static int cc_ReadTocDescr(void)
+{
+	int i;
+	
+	D_S[d].diskstate_flags &= ~toc_bit;
+	clr_cmdbuf();
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_DISKINFO;
+		response_count=6;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_DISKINFO;
+		response_count=6;
+		if(famL_drive)
+			flags_cmd_out=f_putcmd;
+		else
+			flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		/* possibly longer timeout periods necessary */
+		D_S[d].f_multisession=0;
+		drvcmd[0]=CMD2_DISKINFO;
+		drvcmd[1]=0x02;
+		drvcmd[2]=0xAB;
+		drvcmd[3]=0xFF; /* session */
+		response_count=8;
+		flags_cmd_out=f_putcmd;
+	}
+	else if (famT_drive)
+	{
+		D_S[d].f_multisession=0;
+		response_count=12;
+		drvcmd[0]=CMDT_DISKINFO;
+		drvcmd[1]=0x02;
+		drvcmd[6]=CDROM_LEADOUT;
+		drvcmd[8]=response_count;
+		drvcmd[9]=0x00;
+	}
+	i=cmd_out();
+	if (i<0) return (i);
+	if ((fam1_drive)||(fam2_drive)||(famL_drive)||(fam0_drive))
+		D_S[d].xa_byte=infobuf[0];
+	if (fam2_drive)
+	{
+		D_S[d].first_session=infobuf[1];
+		D_S[d].last_session=infobuf[2];
+		D_S[d].n_first_track=infobuf[3];
+		D_S[d].n_last_track=infobuf[4];
+		if (D_S[d].first_session!=D_S[d].last_session)
+		{
+			D_S[d].f_multisession=1;
+			D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7])));
+		}
+#if 0
+		if (D_S[d].first_session!=D_S[d].last_session)
+		{
+			if (D_S[d].last_session<=20)
+				zwanzig=D_S[d].last_session+1;
+			else zwanzig=20;
+			for (count=D_S[d].first_session;count<zwanzig;count++)
+			{
+				drvcmd[0]=CMD2_DISKINFO;
+				drvcmd[1]=0x02;
+				drvcmd[2]=0xAB;
+				drvcmd[3]=count;
+				response_count=8;
+				flags_cmd_out=f_putcmd;
+				i=cmd_out();
+				if (i<0) return (i);
+				D_S[d].msf_multi_n[count]=make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7]));
+			}
+			D_S[d].diskstate_flags |= multisession_bit;
+		}
+#endif
+		drvcmd[0]=CMD2_DISKINFO;
+		drvcmd[1]=0x02;
+		drvcmd[2]=0xAA;
+		drvcmd[3]=0xFF;
+		response_count=5;
+		flags_cmd_out=f_putcmd;
+		i=cmd_out();
+		if (i<0) return (i);
+		D_S[d].size_msf=make32(make16(0,infobuf[2]),make16(infobuf[3],infobuf[4]));
+		D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+	}
+	else if (famT_drive)
+	{
+		D_S[d].size_msf=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
+		D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+		D_S[d].CDsize_frm=D_S[d].size_blk+1;
+		D_S[d].n_first_track=infobuf[2];
+		D_S[d].n_last_track=infobuf[3];
+	}
+	else
+	{
+		D_S[d].n_first_track=infobuf[1];
+		D_S[d].n_last_track=infobuf[2];
+		D_S[d].size_msf=make32(make16(0,infobuf[3]),make16(infobuf[4],infobuf[5]));
+		D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+		if (famL_drive) D_S[d].CDsize_frm=D_S[d].size_blk+1;
+	}
+	D_S[d].diskstate_flags |= toc_bit;
+	msg(DBG_TOC,"TocDesc: %02X %02X %02X %08X\n",
+	    D_S[d].xa_byte,
+	    D_S[d].n_first_track,
+	    D_S[d].n_last_track,
+	    D_S[d].size_msf);
+	return (0);
+}
+/*==========================================================================*/
+static int cc_ReadTocEntry(int num)
+{
+	int i;
+	
+	clr_cmdbuf();
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_READTOC;
+		drvcmd[2]=num;
+		response_count=8;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam2_drive)
+	{
+		/* possibly longer timeout periods necessary */
+		drvcmd[0]=CMD2_DISKINFO;
+		drvcmd[1]=0x02;
+		drvcmd[2]=num;
+		response_count=5;
+		flags_cmd_out=f_putcmd;
+	}
+	else if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_READTOC;
+		drvcmd[1]=0x02;
+		drvcmd[2]=num;
+		response_count=8;
+		if(famL_drive)
+			flags_cmd_out=f_putcmd;
+		else
+	flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (famT_drive)
+	{
+		response_count=12;
+		drvcmd[0]=CMDT_DISKINFO;
+		drvcmd[1]=0x02;
+		drvcmd[6]=num;
+		drvcmd[8]=response_count;
+		drvcmd[9]=0x00;
+	}
+	i=cmd_out();
+	if (i<0) return (i);
+	if ((fam1_drive)||(famL_drive)||(fam0_drive))
+	{
+		D_S[d].TocEnt_nixbyte=infobuf[0];
+		i=1;
+	}
+	else if (fam2_drive) i=0;
+	else if (famT_drive)
+	{
+		i=5;
+	}
+	D_S[d].TocEnt_ctl_adr=swap_nibbles(infobuf[i++]);
+	if ((fam1_drive)||(famL_drive)||(fam0_drive))
+	{
+		D_S[d].TocEnt_number=infobuf[i++];
+		D_S[d].TocEnt_format=infobuf[i];
+	}
+	else D_S[d].TocEnt_number=num;
+	if (fam1_drive) i=4;
+	else if (fam0L_drive) i=5;
+	else if (fam2_drive) i=2;
+	else if (famT_drive) i=9;
+	D_S[d].TocEnt_address=make32(make16(0,infobuf[i]),
+				     make16(infobuf[i+1],infobuf[i+2]));
+	msg(DBG_TOC,"TocEntry: %02X %02X %02X %02X %08X\n",
+	    D_S[d].TocEnt_nixbyte, D_S[d].TocEnt_ctl_adr,
+	    D_S[d].TocEnt_number, D_S[d].TocEnt_format,
+	    D_S[d].TocEnt_address);
+	return (0);
+}
+/*==========================================================================*/
+static int cc_ReadPacket(void)
+{
+	int i;
+	
+	clr_cmdbuf();
+	drvcmd[0]=CMD0_PACKET;
+	drvcmd[1]=response_count;
+	if(famL_drive) flags_cmd_out=f_putcmd;
+	else if (fam01_drive)
+		flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+	else if (fam2_drive) return (-1); /* not implemented yet */
+	else if (famT_drive)
+	{
+		return (-1);
+	}
+	i=cmd_out();
+	return (i);
+}
+/*==========================================================================*/
+static int convert_UPC(u_char *p)
+{
+	int i;
+	
+	p++;
+	if (fam0L_drive) p[13]=0;
+	for (i=0;i<7;i++)
+	{
+		if (fam1_drive) D_S[d].UPC_buf[i]=swap_nibbles(*p++);
+		else if (fam0L_drive)
+		{
+			D_S[d].UPC_buf[i]=((*p++)<<4)&0xFF;
+			D_S[d].UPC_buf[i] |= *p++;
+		}
+		else if (famT_drive)
+		{
+			return (-1);
+		}
+		else /* CD200 */
+		{
+			return (-1);
+		}
+	}
+	D_S[d].UPC_buf[6] &= 0xF0;
+	return (0);
+}
+/*==========================================================================*/
+static int cc_ReadUPC(void)
+{
+	int i;
+#if TEST_UPC
+	int block, checksum;
+#endif TEST_UPC
+	
+	if (fam2_drive) return (0); /* not implemented yet */
+	if (famT_drive)	return (0); /* not implemented yet */
+#if 1
+	if (fam0_drive) return (0); /* but it should work */
+#endif 1
+	
+	D_S[d].diskstate_flags &= ~upc_bit;
+#if TEST_UPC
+	for (block=CD_MSF_OFFSET+1;block<CD_MSF_OFFSET+200;block++)
+	{
+#endif TEST_UPC
+		clr_cmdbuf();
+		if (fam1_drive)
+		{
+			drvcmd[0]=CMD1_READ_UPC;
+#if TEST_UPC
+			drvcmd[1]=(block>>16)&0xFF;
+			drvcmd[2]=(block>>8)&0xFF;
+			drvcmd[3]=block&0xFF;
+#endif TEST_UPC
+			response_count=8;
+			flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+		}
+		else if (fam0L_drive)
+		{
+			drvcmd[0]=CMD0_READ_UPC;
+#if TEST_UPC
+			drvcmd[2]=(block>>16)&0xFF;
+			drvcmd[3]=(block>>8)&0xFF;
+			drvcmd[4]=block&0xFF;
+#endif TEST_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);
+			}
+		}
+#if TEST_UPC
+		checksum=0;
+#endif TEST_UPC
+		for (i=0;i<(fam1_drive?8:16);i++)
+		{
+#if TEST_UPC
+			checksum |= infobuf[i];
+#endif TEST_UPC
+			sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+		}
+		msgbuf[i*3]=0;
+		msg(DBG_UPC,"UPC info:%s\n", msgbuf);
+#if TEST_UPC
+		if ((checksum&0x7F)!=0) break;
+	}
+#endif TEST_UPC
+	D_S[d].UPC_ctl_adr=0;
+	if (fam1_drive) i=0;
+	else i=2;
+	if ((infobuf[i]&0x80)!=0)
+	{
+		convert_UPC(&infobuf[i]);
+		D_S[d].UPC_ctl_adr = (D_S[d].TocEnt_ctl_adr & 0xF0) | 0x02;
+	}
+	for (i=0;i<7;i++)
+		sprintf(&msgbuf[i*3], " %02X", D_S[d].UPC_buf[i]);
+	sprintf(&msgbuf[i*3], " (%02X)", D_S[d].UPC_ctl_adr);
+	msgbuf[i*3+5]=0;
+	msg(DBG_UPC,"UPC code:%s\n", msgbuf);
+	D_S[d].diskstate_flags |= upc_bit;
+	return (0);
+}
+/*==========================================================================*/
+static int cc_CheckMultiSession(void)
+{
+	int i;
+	
+	if (fam2_drive) return (0);
+	D_S[d].f_multisession=0;
+	D_S[d].lba_multi=0;
+	if (fam0_drive) return (0);
+	clr_cmdbuf();
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_MULTISESS;
+		response_count=6;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+		i=cmd_out();
+		if (i<0) return (i);
+		if ((infobuf[0]&0x80)!=0)
+		{
+			D_S[d].f_multisession=1;
+			D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
+							make16(infobuf[2],infobuf[3])));
+		}
+	}
+	else if (famL_drive)
+	{
+		drvcmd[0]=CMDL_MULTISESS;
+		drvcmd[1]=3;
+		drvcmd[2]=1;
+		response_count=8;
+		flags_cmd_out=f_putcmd;
+		i=cmd_out();
+		if (i<0) return (i);
+		D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),
+						make16(infobuf[6],infobuf[7])));
+	}
+	else if (famT_drive)
+	{
+		response_count=12;
+		drvcmd[0]=CMDT_DISKINFO;
+		drvcmd[1]=0x02;
+		drvcmd[6]=0;
+		drvcmd[8]=response_count;
+		drvcmd[9]=0x40;
+		i=cmd_out();
+		if (i<0) return (i);
+		D_S[d].first_session=infobuf[2];
+		D_S[d].last_session=infobuf[3];
+		D_S[d].track_of_last_session=infobuf[6];
+		if (D_S[d].first_session!=D_S[d].last_session)
+		{
+			D_S[d].f_multisession=1;
+			D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
+		}
+	}
+	for (i=0;i<response_count;i++)
+		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+	msgbuf[i*3]=0;
+	msg(DBG_MUL,"MultiSession Info:%s (%d)\n", msgbuf, D_S[d].lba_multi);
+	if (D_S[d].lba_multi>200)
+	{
+		D_S[d].f_multisession=1;
+		msg(DBG_MUL,"MultiSession base: %06X\n", D_S[d].lba_multi);
+	}
+	return (0);
+}
+/*==========================================================================*/
+#if FUTURE
+static int cc_SubChanInfo(int frame, int count, u_char *buffer)
+	/* "frame" is a RED BOOK (msf-bin) address */
+{
+	int i;
+	
+	if (fam0L_drive) return (-ENOSYS); /* drive firmware lacks it */
+	if (famT_drive)
+	{
+		return (-1);
+	}
+#if 0
+	if (D_S[d].audio_state!=audio_playing) return (-ENODATA);
+#endif
+	clr_cmdbuf();
+	drvcmd[0]=CMD1_SUBCHANINF;
+	drvcmd[1]=(frame>>16)&0xFF;
+	drvcmd[2]=(frame>>8)&0xFF;
+	drvcmd[3]=frame&0xFF;
+	drvcmd[5]=(count>>8)&0xFF;
+	drvcmd[6]=count&0xFF;
+	flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+	cmd_type=READ_SC;
+	D_S[d].frame_size=CD_FRAMESIZE_SUB;
+	i=cmd_out(); /* which buffer to use? */
+	return (i);
+}
+#endif FUTURE
+/*==========================================================================*/
+static void check_datarate(void)
+{
+	int i=0;
+	
+	msg(DBG_IOX,"check_datarate entered.\n");
+	datarate=0;
+#if TEST_STI
+	for (i=0;i<=1000;i++) printk(".");
+#endif
+	/* set a timer to make (timed_out_delay!=0) after 1.1 seconds */
+#if 1
+	del_timer(&delay_timer);
+#endif
+	delay_timer.expires=jiffies+11*HZ/10;
+	timed_out_delay=0;
+	add_timer(&delay_timer);
+#if 0
+	msg(DBG_TIM,"delay timer started (11*HZ/10).\n");
+#endif
+	do
+	{
+		i=inb(CDi_status);
+		datarate++;
+#if 1
+		if (datarate>0x6FFFFFFF) break;
+#endif 00000
+	}
+	while (!timed_out_delay);
+	del_timer(&delay_timer);
+#if 0
+	msg(DBG_TIM,"datarate: %04X\n", datarate);
+#endif
+	if (datarate<65536) datarate=65536;
+	maxtim16=datarate*16;
+	maxtim04=datarate*4;
+	maxtim02=datarate*2;
+	maxtim_8=datarate/32;
+#if LONG_TIMING
+	maxtim_data=datarate/100;
+#else
+	maxtim_data=datarate/300;
+#endif LONG_TIMING
+#if 0
+	msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n", maxtim_8, maxtim_data);
+#endif
+}
+/*==========================================================================*/
+#if 0
+static int c2_ReadError(int fam)
+{
+	int i;
+	
+	clr_cmdbuf();
+	response_count=9;
+	clr_respo_buf(9);
+	if (fam==1)
+	{
+		drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
+		i=do_cmd(f_putcmd|f_lopsta|f_getsta|f_ResponseStatus);
+	}
+	else if (fam==2)
+	{
+		drvcmd[0]=CMD2_READ_ERR;
+		i=do_cmd(f_putcmd);
+	}
+	else return (-1);
+	return (i);
+}
+#endif
+/*==========================================================================*/
+static void ask_mail(void)
+{
+	int i;
+	
+	msg(DBG_INF, "please mail the following lines to emoenke@gwdg.de:\n");
+	msg(DBG_INF, "%s\n", VERSION);
+	msg(DBG_INF, "address %03X, type %s, drive %s (ID %d)\n",
+	    CDo_command, type, D_S[d].drive_model, D_S[d].drv_id);
+	for (i=0;i<12;i++)
+		sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+	msgbuf[i*3]=0;
+	msg(DBG_INF,"infobuf =%s\n", msgbuf);
+	for (i=0;i<12;i++)
+		sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
+	msgbuf[i*3]=0;
+	msg(DBG_INF,"infobuf =%s\n", msgbuf);
+}
+/*==========================================================================*/
+static int check_version(void)
+{
+	int i, j, l;
+	int teac_possible=0;
+	
+	msg(DBG_INI,"check_version entered.\n");
+	msg(DBG_TE2,"check_version: id=%d, d=%d.\n", D_S[d].drv_id, d);
+	D_S[d].drv_type=0;
+
+	/* check for CR-52x, CR-56x and LCS-7260 */
+	/* clear any pending error state */
+	clr_cmdbuf();
+	drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
+	response_count=9;
+	flags_cmd_out=f_putcmd;
+	i=cmd_out();
+	if (i<0) msg(DBG_INI,"CMD0_READERR returns %d (ok anyway).\n",i);
+	/* read drive version */
+	clr_cmdbuf();
+	for (i=0;i<12;i++) infobuf[i]=0;
+	drvcmd[0]=CMD0_READ_VER; /* same as CMD1_ and CMDL_ */
+	response_count=12; /* fam1: only 11 */
+	flags_cmd_out=f_putcmd;
+	i=cmd_out();
+	if (i<-1) msg(DBG_INI,"CMD0_READ_VER returns %d\n",i);
+	if (i==-11) teac_possible++;
+	j=0;
+	for (i=0;i<12;i++) j+=infobuf[i];
+	if (j)
+	{
+		for (i=0;i<12;i++)
+			sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+		msgbuf[i*3]=0;
+		msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+		msg(DBG_000,"infobuf =%s\n", msgbuf);
+		for (i=0;i<12;i++)
+			sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
+		msgbuf[i*3]=0;
+		msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+		msg(DBG_000,"infobuf =%s\n", msgbuf);
+	}
+	for (i=0;i<4;i++) if (infobuf[i]!=family1[i]) break;
+	if (i==4)
+	{
+		D_S[d].drive_model[0]='C';
+		D_S[d].drive_model[1]='R';
+		D_S[d].drive_model[2]='-';
+		D_S[d].drive_model[3]='5';
+		D_S[d].drive_model[4]=infobuf[i++];
+		D_S[d].drive_model[5]=infobuf[i++];
+		D_S[d].drive_model[6]=0;
+		D_S[d].drv_type=drv_fam1;
+	}
+	if (!D_S[d].drv_type)
+	{
+		for (i=0;i<8;i++) if (infobuf[i]!=family0[i]) break;
+		if (i==8)
+		{
+			D_S[d].drive_model[0]='C';
+			D_S[d].drive_model[1]='R';
+			D_S[d].drive_model[2]='-';
+			D_S[d].drive_model[3]='5';
+			D_S[d].drive_model[4]='2';
+			D_S[d].drive_model[5]='x';
+			D_S[d].drive_model[6]=0;
+			D_S[d].drv_type=drv_fam0;
+		}
+	}
+	if (!D_S[d].drv_type)
+	{
+		for (i=0;i<8;i++) if (infobuf[i]!=familyL[i]) break;
+		if (i==8)
+		{
+			for (j=0;j<8;j++)
+				D_S[d].drive_model[j]=infobuf[j];
+			D_S[d].drive_model[8]=0;
+			D_S[d].drv_type=drv_famL;
+		}
+	}
+	if (!D_S[d].drv_type)
+	{
+		/* check for CD200 */
+		clr_cmdbuf();
+		drvcmd[0]=CMD2_READ_ERR;
+		response_count=9;
+		flags_cmd_out=f_putcmd;
+		i=cmd_out();
+		if (i<0) msg(DBG_INI,"CMD2_READERR returns %d (ok anyway).\n",i);
+		if (i<0) msg(DBG_000,"CMD2_READERR returns %d (ok anyway).\n",i);
+		/* read drive version */
+		clr_cmdbuf();
+		for (i=0;i<12;i++) infobuf[i]=0;
+		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+#if 0
+		OUT(CDo_reset,0);
+		sbp_sleep(6*HZ);
+		OUT(CDo_enable,D_S[d].drv_sel);
+#endif 0
+		drvcmd[0]=CMD2_READ_VER;
+		response_count=12;
+		flags_cmd_out=f_putcmd;
+		i=cmd_out();
+		if (i<0) msg(DBG_INI,"CMD2_READ_VER returns %d\n",i);
+		if (i==-7) teac_possible++;
+		j=0;
+		for (i=0;i<12;i++) j+=infobuf[i];
+		if (j)
+		{
+			for (i=0;i<12;i++)
+				sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+			msgbuf[i*3]=0;
+			msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+			for (i=0;i<12;i++)
+				sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
+			msgbuf[i*3]=0;
+			msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+		}
+		if (i>=0)
+		{
+			for (i=0;i<5;i++) if (infobuf[i]!=family2[i]) break;
+			if (i==5)
+			{
+				D_S[d].drive_model[0]='C';
+				D_S[d].drive_model[1]='D';
+				D_S[d].drive_model[2]='2';
+				D_S[d].drive_model[3]='0';
+				D_S[d].drive_model[4]='0';
+				D_S[d].drive_model[5]=infobuf[i++];
+				D_S[d].drive_model[6]=infobuf[i++];
+				D_S[d].drive_model[7]=0;
+				D_S[d].drv_type=drv_fam2;
+			}
+		}
+	}
+	if (!D_S[d].drv_type)
+	{
+		/* check for TEAC CD-55A */
+		msg(DBG_TEA,"teac_possible: %d\n",teac_possible);
+		for (j=1;j<=((D_S[d].drv_id==0)?3:1);j++)
+		{
+			for (l=1;l<=((D_S[d].drv_id==0)?10:1);l++)
+			{
+				msg(DBG_TEA,"TEAC reset #%d-%d.\n", j, l);
+				if (sbpro_type==1) OUT(CDo_reset,0);
+				else
+				{
+					OUT(CDo_enable,D_S[d].drv_sel);
+					OUT(CDo_sel_i_d,0);
+					OUT(CDo_command,CMDT_RESET);
+					for (i=0;i<9;i++) OUT(CDo_command,0);
+				}
+				sbp_sleep(5*HZ/10);
+				OUT(CDo_enable,D_S[d].drv_sel);
+				OUT(CDo_sel_i_d,0);
+				i=inb(CDi_status);
+				msg(DBG_TEA,"TEAC CDi_status: %02X.\n",i);
+#if 0
+				if (i&s_not_result_ready) continue; /* drive not present or ready */
+#endif
+				i=inb(CDi_info);
+				msg(DBG_TEA,"TEAC CDi_info: %02X.\n",i);
+				if (i==0x55) break; /* drive found */
+			}
+			if (i==0x55) break; /* drive found */
+		}
+		if (i==0x55) /* drive found */
+		{
+			msg(DBG_TEA,"TEAC drive found.\n");
+			clr_cmdbuf();
+			flags_cmd_out=f_putcmd;
+			response_count=12;
+			drvcmd[0]=CMDT_READ_VER;
+			drvcmd[4]=response_count;
+			for (i=0;i<12;i++) infobuf[i]=0;
+			i=cmd_out_T();
+			if (i!=0) msg(DBG_TEA,"cmd_out_T(CMDT_READ_VER) returns %d.\n",i);
+			for (i=1;i<6;i++) if (infobuf[i]!=familyT[i-1]) break;
+			if (i==6)
+			{
+				D_S[d].drive_model[0]='C';
+				D_S[d].drive_model[1]='D';
+				D_S[d].drive_model[2]='-';
+				D_S[d].drive_model[3]='5';
+				D_S[d].drive_model[4]='5';
+				D_S[d].drive_model[5]=0;
+				D_S[d].drv_type=drv_famT;
+			}
+		}
+	}
+	if (!D_S[d].drv_type)
+	{
+		msg(DBG_TEA,"no drive found at address %03X under ID %d.\n",CDo_command,D_S[d].drv_id);
+		return (-522);
+	}
+	for (j=0;j<4;j++) D_S[d].firmware_version[j]=infobuf[i+j];
+	if (famL_drive)
+	{
+		u_char lcs_firm_e1[]="A E1";
+		u_char lcs_firm_f4[]="A4F4";
+		
+		for (j=0;j<4;j++)
+			if (D_S[d].firmware_version[j]!=lcs_firm_e1[j]) break;
+		if (j==4) D_S[d].drv_type=drv_e1;
+
+		for (j=0;j<4;j++)
+			if (D_S[d].firmware_version[j]!=lcs_firm_f4[j]) break;
+		if (j==4) D_S[d].drv_type=drv_f4;
+
+		if (D_S[d].drv_type==drv_famL) ask_mail();
+	}
+	else if (famT_drive)
+	{
+		j=infobuf[4]; /* one-byte version??? - here: 0x15 */
+		if (j=='5')
+		{
+			D_S[d].firmware_version[0]=infobuf[7];
+			D_S[d].firmware_version[1]=infobuf[8];
+			D_S[d].firmware_version[2]=infobuf[10];
+			D_S[d].firmware_version[3]=infobuf[11];
+		}
+		else
+		{
+			if (j!=0x15) ask_mail();
+			D_S[d].firmware_version[0]='0';
+			D_S[d].firmware_version[1]='.';
+			D_S[d].firmware_version[2]='0'+(j>>4);
+			D_S[d].firmware_version[3]='0'+(j&0x0f);
+		}
+	}
+	else /* CR-52x, CR-56x, CD200 */
+	{
+		j = (D_S[d].firmware_version[0] & 0x0F) * 100 +
+			(D_S[d].firmware_version[2] & 0x0F) *10 +
+				(D_S[d].firmware_version[3] & 0x0F);
+		if (fam0_drive)
+		{
+			if (j<200) D_S[d].drv_type=drv_199;
+			else if (j<201) D_S[d].drv_type=drv_200;
+			else if (j<210) D_S[d].drv_type=drv_201;
+			else if (j<211) D_S[d].drv_type=drv_210;
+			else if (j<300) D_S[d].drv_type=drv_211;
+			else if (j>=300) D_S[d].drv_type=drv_300;
+		}
+		else if (fam1_drive)
+		{
+			if (j<100) D_S[d].drv_type=drv_099;
+			else
+			{
+				D_S[d].drv_type=drv_100;
+				if ((j!=500)&&(j!=102)) ask_mail();
+			}
+		}
+		else if (fam2_drive)
+		{
+			msg(DBG_INF,"new drive CD200 (%s)detected.\n", D_S[d].firmware_version);
+			msg(DBG_INF,"CD200 is not fully supported yet - CD200F should work.\n");
+			if ((j!=1)&&(j!=101)&&(j!=35)) ask_mail(); /* unknown version at time */
+		}
+	}
+	msg(DBG_LCS,"drive type %02X\n",D_S[d].drv_type);
+	msg(DBG_INI,"check_version done.\n");
+	return (0);
+}
+/*==========================================================================*/
+static void switch_drive(int i)
+{
+	d=i;
+	OUT(CDo_enable,D_S[d].drv_sel);
+	msg(DBG_DID,"drive %d (ID=%d) activated.\n", i, D_S[d].drv_id);
+	return;
+}
+/*==========================================================================*/
+#ifdef PATH_CHECK
+/*
+ * probe for the presence of an interface card
+ */
+static int check_card(int port)
+{
+#undef N_RESPO
+#define N_RESPO 20
+	int i, j, k;
+	u_char response[N_RESPO];
+	u_char save_port0;
+	u_char save_port3;
+	
+	msg(DBG_INI,"check_card entered.\n");
+	save_port0=inb(port+0);
+	save_port3=inb(port+3);
+	
+	for (j=0;j<NR_SBPCD;j++)
+	{
+		OUT(port+3,j) ; /* enable drive #j */
+		OUT(port+0,CMD0_PATH_CHECK);
+		for (i=10;i>0;i--) OUT(port+0,0);
+		for (k=0;k<N_RESPO;k++) response[k]=0;
+		for (k=0;k<N_RESPO;k++)
+		{
+			for (i=10000;i>0;i--)
+			{
+				if (inb(port+1)&s_not_result_ready) continue;
+				response[k]=inb(port+0);
+				break;
+			}
+		}
+		for (i=0;i<N_RESPO;i++)
+			sprintf(&msgbuf[i*3], " %02X", response[i]);
+		msgbuf[i*3]=0;
+		msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
+		OUT(port+0,CMD0_PATH_CHECK);
+		for (i=10;i>0;i--) OUT(port+0,0);
+		for (k=0;k<N_RESPO;k++) response[k]=0xFF;
+		for (k=0;k<N_RESPO;k++)
+		{
+			for (i=10000;i>0;i--)
+			{
+				if (inb(port+1)&s_not_result_ready) continue;
+				response[k]=inb(port+0);
+				break;
+			}
+		}
+		for (i=0;i<N_RESPO;i++)
+			sprintf(&msgbuf[i*3], " %02X", response[i]);
+		msgbuf[i*3]=0;
+		msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
+
+		if (response[0]==0xAA)
+			if (response[1]==0x55)
+				return (0);
+	}
+	for (j=0;j<NR_SBPCD;j++)
+	{
+		OUT(port+3,j) ; /* enable drive #j */
+		OUT(port+0,CMD2_READ_VER);
+		for (i=10;i>0;i--) OUT(port+0,0);
+		for (k=0;k<N_RESPO;k++) response[k]=0;
+		for (k=0;k<N_RESPO;k++)
+		{
+			for (i=1000000;i>0;i--)
+			{
+				if (inb(port+1)&s_not_result_ready) continue;
+				response[k]=inb(port+0);
+				break;
+			}
+		}
+		for (i=0;i<N_RESPO;i++)
+			sprintf(&msgbuf[i*3], " %02X", response[i]);
+		msgbuf[i*3]=0;
+		msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
+
+		OUT(port+0,CMD2_READ_VER);
+		for (i=10;i>0;i--) OUT(port+0,0);
+		for (k=0;k<N_RESPO;k++) response[k]=0xFF;
+		for (k=0;k<N_RESPO;k++)
+		{
+			for (i=1000000;i>0;i--)
+			{
+				if (inb(port+1)&s_not_result_ready) continue;
+				response[k]=inb(port+0);
+				break;
+			}
+		}
+		for (i=0;i<N_RESPO;i++)
+			sprintf(&msgbuf[i*3], " %02X", response[i]);
+		msgbuf[i*3]=0;
+		msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
+
+		if (response[0]==0xAA)
+			if (response[1]==0x55)
+				return (0);
+	}
+	OUT(port+0,save_port0);
+	OUT(port+3,save_port3);
+	return (0); /* in any case - no real "function" at time */
+}
+#endif PATH_CHECK
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * probe for the presence of drives on the selected controller
+ */
+static int check_drives(void)
+{
+	int i, j;
+	
+	msg(DBG_INI,"check_drives entered.\n");
+	ndrives=0;
+	for (j=0;j<MAX_DRIVES;j++)
+	{
+		D_S[ndrives].drv_id=j;
+		if (sbpro_type==1) D_S[ndrives].drv_sel=(j&0x01)<<1|(j&0x02)>>1;
+		else D_S[ndrives].drv_sel=j;
+		switch_drive(ndrives);
+		msg(DBG_INI,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
+		msg(DBG_000,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
+		i=check_version();
+		if (i<0) msg(DBG_INI,"check_version returns %d.\n",i);
+		else
+		{
+			D_S[d].drv_options=drv_pattern[j];
+			if (fam0L_drive) D_S[d].drv_options&=~(speed_auto|speed_300|speed_150);
+			msg(DBG_INF, "Drive %d (ID=%d): %.9s (%.4s) at 0x%03X (type %d)\n",
+			    d,
+			    D_S[d].drv_id,
+			    D_S[d].drive_model,
+			    D_S[d].firmware_version,
+			    CDo_command,
+			    sbpro_type);
+			ndrives++;
+		}
+	}
+	for (j=ndrives;j<NR_SBPCD;j++) D_S[j].drv_id=-1;
+	if (ndrives==0) return (-1);
+	return (0);
+}
+/*==========================================================================*/
+#if FUTURE
+/*
+ *  obtain if requested service disturbs current audio state
+ */            
+static int obey_audio_state(u_char audio_state, u_char func,u_char subfunc)
+{
+	switch (audio_state)                   /* audio status from controller  */
+	{
+	case aud_11: /* "audio play in progress" */
+	case audx11:
+		switch (func)                      /* DOS command code */
+		{
+		case cmd_07: /* input flush  */
+		case cmd_0d: /* open device  */
+		case cmd_0e: /* close device */
+		case cmd_0c: /* ioctl output */
+			return (1);
+		case cmd_03: /* ioctl input  */
+			switch (subfunc)
+				/* DOS ioctl input subfunction */
+			{
+			case cxi_00:
+			case cxi_06:
+			case cxi_09:
+				return (1);
+			default:
+				return (ERROR15);
+			}
+			return (1);
+		default:
+			return (ERROR15);
+		}
+		return (1);
+	case aud_12:                  /* "audio play paused"      */
+	case audx12:
+		return (1);
+	default:
+		return (2);
+	}
+}
+/*==========================================================================*/
+/* allowed is only
+ * ioctl_o, flush_input, open_device, close_device, 
+ * tell_address, tell_volume, tell_capabiliti,
+ * tell_framesize, tell_CD_changed, tell_audio_posi
+ */
+static int check_allowed1(u_char func1, u_char func2)
+{
+#if 000
+	if (func1==ioctl_o) return (0);
+	if (func1==read_long) return (-1);
+	if (func1==read_long_prefetch) return (-1);
+	if (func1==seek) return (-1);
+	if (func1==audio_play) return (-1);
+	if (func1==audio_pause) return (-1);
+	if (func1==audio_resume) return (-1);
+	if (func1!=ioctl_i) return (0);
+	if (func2==tell_SubQ_run_tot) return (-1);
+	if (func2==tell_cdsize) return (-1);
+	if (func2==tell_TocDescrip) return (-1);
+	if (func2==tell_TocEntry) return (-1);
+	if (func2==tell_subQ_info) return (-1);
+	if (fam1_drive) if (func2==tell_SubChanInfo) return (-1);
+	if (func2==tell_UPC) return (-1);
+#else
+	return (0);
+#endif 000
+}
+/*==========================================================================*/
+static int check_allowed2(u_char func1, u_char func2)
+{
+#if 000
+	if (func1==read_long) return (-1);
+	if (func1==read_long_prefetch) return (-1);
+	if (func1==seek) return (-1);
+	if (func1==audio_play) return (-1);
+  if (func1!=ioctl_o) return (0);
+	if (fam1_drive)
+	{
+		if (func2==EjectDisk) return (-1);
+		if (func2==CloseTray) return (-1);
+	}
+#else
+	return (0);
+#endif 000
+}
+/*==========================================================================*/
+static int check_allowed3(u_char func1, u_char func2)
+{
+#if 000
+	if (func1==ioctl_i)
+	{
+		if (func2==tell_address) return (0);
+		if (func2==tell_capabiliti) return (0);
+		if (func2==tell_CD_changed) return (0);
+		if (fam0L_drive) if (func2==tell_SubChanInfo) return (0);
+		return (-1);
+	}
+	if (func1==ioctl_o)
+	{
+		if (func2==DriveReset) return (0);
+		if (fam0L_drive)
+		{
+			if (func2==EjectDisk) return (0);
+			if (func2==LockDoor) return (0);
+	  if (func2==CloseTray) return (0);
+		}
+		return (-1);
+    }
+	if (func1==flush_input) return (-1);
+	if (func1==read_long) return (-1);
+	if (func1==read_long_prefetch) return (-1);
+	if (func1==seek) return (-1);
+	if (func1==audio_play) return (-1);
+	if (func1==audio_pause) return (-1);
+	if (func1==audio_resume) return (-1);
+#else
+	return (0);
+#endif 000
+}
+/*==========================================================================*/
+static int seek_pos_audio_end(void)
+{
+	int i;
+
+	i=msf2blk(D_S[d].pos_audio_end)-1;
+	if (i<0) return (-1);
+	i=cc_Seek(i,0);
+	return (i);
+}
+#endif FUTURE
+/*==========================================================================*/
+static int ReadToC(void)
+{
+	int i, j;
+	D_S[d].diskstate_flags &= ~toc_bit;
+	D_S[d].ored_ctl_adr=0;
+	for (j=D_S[d].n_first_track;j<=D_S[d].n_last_track;j++)
+	{
+		i=cc_ReadTocEntry(j);
+		if (i<0)
+		{
+			msg(DBG_INF,"cc_ReadTocEntry(%d) returns %d.\n",j,i);
+			return (i);
+		}
+		D_S[d].TocBuffer[j].nixbyte=D_S[d].TocEnt_nixbyte;
+		D_S[d].TocBuffer[j].ctl_adr=D_S[d].TocEnt_ctl_adr;
+		D_S[d].TocBuffer[j].number=D_S[d].TocEnt_number;
+		D_S[d].TocBuffer[j].format=D_S[d].TocEnt_format;
+		D_S[d].TocBuffer[j].address=D_S[d].TocEnt_address;
+		D_S[d].ored_ctl_adr |= D_S[d].TocEnt_ctl_adr;
+	}
+	/* fake entry for LeadOut Track */
+	D_S[d].TocBuffer[j].nixbyte=0;
+	D_S[d].TocBuffer[j].ctl_adr=0;
+	D_S[d].TocBuffer[j].number=CDROM_LEADOUT;
+	D_S[d].TocBuffer[j].format=0;
+	D_S[d].TocBuffer[j].address=D_S[d].size_msf;
+	
+	D_S[d].diskstate_flags |= toc_bit;
+	return (0);
+}
+/*==========================================================================*/
+static int DiskInfo(void)
+{
+	int i, j;
+	
+	D_S[d].mode=READ_M1;
+	
+#undef LOOP_COUNT
+#define LOOP_COUNT 10 /* needed for some "old" drives */
+	
+	msg(DBG_000,"DiskInfo entered.\n");
+	for (j=1;j<LOOP_COUNT;j++)
+	{
+		i=SetSpeed();
+		if (i<0)
+		{
+			msg(DBG_INF,"DiskInfo: SetSpeed returns %d\n", i);
+			continue;
+		}
+		i=cc_ModeSense();
+		if (i<0)
+		{
+			msg(DBG_INF,"DiskInfo: cc_ModeSense returns %d\n", i);
+			continue;
+		}
+		i=cc_ReadCapacity();
+		if (i>=0) break;
+		msg(DBG_INF,"DiskInfo: ReadCapacity #%d returns %d\n", j, i);
+		i=cc_DriveReset();
+	}
+	if (j==LOOP_COUNT) return (-33); /* give up */
+	
+	i=cc_ReadTocDescr();
+	if (i<0)
+	{
+		msg(DBG_INF,"DiskInfo: ReadTocDescr returns %d\n", i);
+		return (i);
+	}
+	i=ReadToC();
+	if (i<0)
+	{
+		msg(DBG_INF,"DiskInfo: ReadToC returns %d\n", i);
+		return (i);
+	}
+	i=cc_CheckMultiSession();
+	if (i<0)
+	{
+		msg(DBG_INF,"DiskInfo: cc_CheckMultiSession returns %d\n", i);
+		return (i);
+	}
+	if (D_S[d].f_multisession) D_S[d].sbp_bufsiz=1;  /* possibly a weird PhotoCD */
+	else D_S[d].sbp_bufsiz=SBP_BUFFER_FRAMES;
+	i=cc_ReadTocEntry(D_S[d].n_first_track);
+	if (i<0)
+	{
+		msg(DBG_INF,"DiskInfo: cc_ReadTocEntry(1) returns %d\n", i);
+		return (i);
+	}
+	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))
+	{
+		/* XA disk with old drive */
+		cc_ModeSelect(CD_FRAMESIZE_RAW1);
+		cc_ModeSense();
+	}
+	if (famT_drive)	cc_prep_mode_T();
+	msg(DBG_000,"DiskInfo done.\n");
+	return (0);
+}
+/*==========================================================================*/
+#if FUTURE
+/*
+ *  called always if driver gets entered
+ *  returns 0 or ERROR2 or ERROR15
+ */
+static int prepare(u_char func, u_char subfunc)
+{
+	int i;
+	
+	if (fam0L_drive)
+	{
+		i=inb(CDi_status);
+		if (i&s_attention) GetStatus();
+	}
+	else if (fam1_drive) GetStatus();
+	else if (fam2_drive) GetStatus();
+	else if (famT_drive) GetStatus();
+	if (D_S[d].CD_changed==0xFF)
+	{
+		D_S[d].diskstate_flags=0;
+		D_S[d].audio_state=0;
+		if (!st_diskok)
+		{
+			i=check_allowed1(func,subfunc);
+			if (i<0) return (-2);
+		}
+		else 
+		{
+			i=check_allowed3(func,subfunc);
+			if (i<0)
+			{
+				D_S[d].CD_changed=1;
+				return (-15);
+			}
+		}
+	}
+	else
+	{
+		if (!st_diskok)
+		{
+			D_S[d].diskstate_flags=0;
+			D_S[d].audio_state=0;
+			i=check_allowed1(func,subfunc);
+			if (i<0) return (-2);
+		}
+		else
+		{ 
+			if (st_busy)
+			{
+				if (D_S[d].audio_state!=audio_pausing)
+				{
+					i=check_allowed2(func,subfunc);
+					if (i<0) return (-2);
+				}
+			}
+			else
+			{
+				if (D_S[d].audio_state==audio_playing) seek_pos_audio_end();
+				D_S[d].audio_state=0;
+			}
+			if (!frame_size_valid)
+			{
+				i=DiskInfo();
+				if (i<0)
+				{
+					D_S[d].diskstate_flags=0;
+					D_S[d].audio_state=0;
+					i=check_allowed1(func,subfunc);
+					if (i<0) return (-2);
+				}
+			}
+		}
+    }
+	return (0);
+}
+#endif FUTURE
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * Check the results of the "get status" command.
+ */
+static int sbp_status(void)
+{
+	int st;
+	
+	st=ResponseStatus();
+	if (st<0)
+	{
+		msg(DBG_INF,"sbp_status: timeout.\n");
+		return (0);
+	}
+	
+	if (!st_spinning) msg(DBG_SPI,"motor got off - ignoring.\n");
+	
+	if (st_check) 
+	{
+		msg(DBG_INF,"st_check detected - retrying.\n");
+		return (0);
+	}
+	if (!st_door_closed)
+	{
+		msg(DBG_INF,"door is open - retrying.\n");
+		return (0);
+	}
+	if (!st_caddy_in)
+	{
+		msg(DBG_INF,"disk removed - retrying.\n");
+		return (0);
+	}
+	if (!st_diskok) 
+	{
+		msg(DBG_INF,"!st_diskok detected - retrying.\n");
+		return (0);
+	}
+	if (st_busy) 
+	{
+		msg(DBG_INF,"st_busy detected - retrying.\n");
+		return (0);
+	}
+	return (1);
+}
+/*==========================================================================*/
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * ioctl support
+ */
+static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg)
+{
+	int i, st;
+	
+	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);
+	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
+	{
+		msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev);
+		return (-ENXIO);             /* no such drive */
+	}
+	if (d!=i) switch_drive(i);
+	
+#if 0
+	st=GetStatus();
+	if (st<0) return (-EIO);
+	
+	if (!toc_valid)
+	{
+		i=DiskInfo();
+		if (i<0) return (-EIO);	/* error reading TOC */
+	}
+#endif
+	
+	msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd);
+	switch (cmd) 		/* Sun-compatible */
+	{
+	case DDIOCSDBG:		/* DDI Debug */
+		if (!suser()) return (-EPERM);
+		i=sbpcd_dbg_ioctl(arg,1);
+		return (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 (-EIO);
+			if (famL_drive) i=cc_Pause_Resume(1);
+			else i=cc_ReadSubQ();
+			if (i<0) return (-EIO);
+			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
+			D_S[d].audio_state=audio_pausing;
+			return (0);
+		case audio_pausing:
+			i=cc_Seek(D_S[d].pos_audio_start,1);
+			if (i<0) return (-EIO);
+			return (0);
+		default:
+			return (-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 (-EIO);
+		D_S[d].audio_state=audio_playing;
+		return (0);
+		
+	case CDROMPLAYMSF:
+		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
+		if (D_S[d].audio_state==audio_playing)
+		{
+			i=cc_Pause_Resume(1);
+			if (i<0) return (-EIO);
+			i=cc_ReadSubQ();
+			if (i<0) return (-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 (st);
+		memcpy_fromfs(&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);
+		msg(DBG_IOC,"ioctl: cc_PlayAudio returns %d\n",i);
+#if 0
+		if (i<0) return (-EIO);
+#endif 0
+		D_S[d].audio_state=audio_playing;
+		return (0);
+		
+	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
+		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
+		if (D_S[d].audio_state==audio_playing)
+		{
+			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
+			return (0);
+			return (-EINVAL);
+		}
+		st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
+		if (st<0)
+		{
+			msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n");
+			return (st);
+		}
+		memcpy_fromfs(&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 (-EINVAL);
+		if (ti.cdti_trk0>D_S[d].n_last_track) return (-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 0
+		if (i<0) return (-EIO);
+#endif 0
+		D_S[d].audio_state=audio_playing;
+		return (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 (st);
+		memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
+		return (0);
+		
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
+		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_tocentry));
+		if (st) return (st);
+		memcpy_fromfs(&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 (-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 (-EINVAL);
+		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
+		if (st) return (st);
+		memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
+		return (0);
+		
+	case CDROMRESET:      /* hard reset the drive */
+		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
+		i=DriveReset();
+		D_S[d].audio_state=0;
+		return (i);
+		
+	case CDROMSTOP:      /* Spin down the drive */
+		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
+		i=cc_Pause_Resume(1);
+		D_S[d].audio_state=0;
+		return (i);
+		
+	case CDROMSTART:  /* Spin up the drive */
+		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
+		cc_SpinUp();
+		D_S[d].audio_state=0;
+		return (0);
+		
+	case CDROMEJECT:
+		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
+		if (fam0_drive) return (0);
+		if (D_S[d].open_count>1) return (-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 (-EIO);
+		D_S[d].CD_changed=0xFF;
+		D_S[d].diskstate_flags=0;
+		D_S[d].audio_state=0;
+		return (0);
+		
+	case CDROMEJECT_SW:
+		msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
+		if (fam0_drive) return (0);
+		D_S[d].f_eject=arg;
+		return (0);
+		
+	case CDROMVOLCTRL:   /* Volume control */
+		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
+		st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
+		if (st) return (st);
+		memcpy_fromfs(&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 (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 (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;
+		memcpy_tofs((void *)arg,&volctrl,sizeof(volctrl));
+		return (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 (-EIO);
+					    }
+		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
+		if (st)	return (st);
+		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_subchnl));
+		if (st) return (st);
+		memcpy_fromfs(&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_tofs((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 (0);
+		
+	case CDROMREADMODE1:
+		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
+		cc_ModeSelect(CD_FRAMESIZE);
+		cc_ModeSense();
+		D_S[d].mode=READ_M1;
+		return (0);
+		
+	case CDROMREADMODE2: /* not usable at the moment */
+		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
+		cc_ModeSelect(CD_FRAMESIZE_RAW1);
+		cc_ModeSense();
+		D_S[d].mode=READ_M2;
+		return (0);
+		
+	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
+		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
+#ifdef MODULE
+		if (D_S[d].sbp_audsiz>0)
+			vfree(D_S[d].aud_buf);
+#endif MODULE
+		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);
+		}
+		return (D_S[d].sbp_audsiz);
+
+	case CDROMREADAUDIO:
+	{ /* start of CDROMREADAUDIO */
+		int i=0, j=0, frame, block;
+		u_int try=0;
+		u_long timeout;
+		u_char *p;
+		u_int data_tries = 0;
+		u_int data_waits = 0;
+		u_int data_retrying = 0;
+		int status_tries;
+		int error_flag;
+		
+		msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
+		if (fam0_drive) return (-EINVAL);
+		if (famL_drive) return (-EINVAL);
+		if (fam2_drive) return (-EINVAL);
+		if (famT_drive) return (-EINVAL);
+		if (D_S[d].aud_buf==NULL) return (-EINVAL);
+		i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
+		if (i) return (i);
+		memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
+		if (read_audio.nframes>D_S[d].sbp_audsiz) return (-EINVAL);
+		i=verify_area(VERIFY_WRITE, read_audio.buf,
+			      read_audio.nframes*CD_FRAMESIZE_RAW);
+		if (i) return (i);
+		
+		if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
+			block=msf2lba(&read_audio.addr.msf.minute);
+		else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
+			block=read_audio.addr.lba;
+		else return (-EINVAL);
+		i=cc_SetSpeed(speed_150,0,0);
+		if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
+		msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
+		    block, blk2msf(block));
+		msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
+		while (busy_data) sbp_sleep(HZ/10); /* wait a bit */
+		busy_audio=1;
+		error_flag=0;
+		for (data_tries=5; data_tries>0; data_tries--)
+		{
+			msg(DBG_AUD,"data_tries=%d ...\n", data_tries);
+			D_S[d].mode=READ_AU;
+			cc_ModeSelect(CD_FRAMESIZE_RAW);
+			cc_ModeSense();
+			for (status_tries=3; status_tries > 0; status_tries--)
+			{
+				flags_cmd_out |= f_respo3;
+				cc_ReadStatus();
+				if (sbp_status() != 0) break;
+				sbp_sleep(1);    /* wait a bit, try again */
+			}
+			if (status_tries == 0)
+			{
+				msg(DBG_AUD,"read_audio: sbp_status: failed after 3 tries.\n");
+				continue;
+			}
+			msg(DBG_AUD,"read_audio: sbp_status: ok.\n");
+			
+			flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
+			if (fam0L_drive)
+			{
+				flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
+				cmd_type=READ_M2;
+				drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
+				drvcmd[1]=(block>>16)&0x000000ff;
+				drvcmd[2]=(block>>8)&0x000000ff;
+				drvcmd[3]=block&0x000000ff;
+				drvcmd[4]=0;
+				drvcmd[5]=read_audio.nframes; /* # of frames */
+				drvcmd[6]=0;
+			}
+			else if (fam1_drive)
+			{
+				drvcmd[0]=CMD1_READ; /* "read frames", new drives */
+				lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+				drvcmd[4]=0;
+				drvcmd[5]=0;
+				drvcmd[6]=read_audio.nframes; /* # of frames */
+			}
+			else if (fam2_drive) /* CD200: not tested yet */
+			{
+			}
+			else if (famT_drive) /* CD-55A: not tested yet */
+			{
+			}
+			msg(DBG_AUD,"read_audio: before giving \"read\" command.\n");
+			for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+			sbp_sleep(0);
+			msg(DBG_AUD,"read_audio: after giving \"read\" command.\n");
+			for (frame=1;frame<2 && !error_flag; frame++)
+			{
+				try=maxtim_data;
+				for (timeout=jiffies+9*HZ; ; )
+				{
+					for ( ; try!=0;try--)
+					{
+						j=inb(CDi_status);
+						if (!(j&s_not_data_ready)) break;
+						if (!(j&s_not_result_ready)) break;
+						if (fam0L_drive) if (j&s_attention) break;
+					}
+					if (try != 0 || timeout <= jiffies) break;
+					if (data_retrying == 0) data_waits++;
+					data_retrying = 1;
+					sbp_sleep(1);
+					try = 1;
+				}
+				if (try==0)
+				{
+					msg(DBG_INF,"read_audio: sbp_data: CDi_status timeout.\n");
+					error_flag++;
+					break;
+				}
+				msg(DBG_AUD,"read_audio: sbp_data: CDi_status ok.\n");
+				if (j&s_not_data_ready)
+				{
+					msg(DBG_INF, "read_audio: sbp_data: DATA_READY timeout.\n");
+					error_flag++;
+					break;
+				}
+				msg(DBG_AUD,"read_audio: before reading data.\n");
+				error_flag=0;
+				p = D_S[d].aud_buf;
+				if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+				insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
+				if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+				data_retrying = 0;
+			}
+			msg(DBG_AUD,"read_audio: after reading data.\n");
+			if (error_flag)    /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
+			{
+				msg(DBG_AUD,"read_audio: read aborted by drive\n");
+#if 0000
+				i=cc_DriveReset();                /* ugly fix to prevent a hang */
+#else
+				i=cc_ReadError();
+#endif 0000
+				continue;
+			}
+			if (fam0L_drive)
+			{
+				i=maxtim_data;
+				for (timeout=jiffies+9*HZ; timeout > jiffies; timeout--)
+				{
+					for ( ;i!=0;i--)
+					{
+						j=inb(CDi_status);
+						if (!(j&s_not_data_ready)) break;
+						if (!(j&s_not_result_ready)) break;
+						if (j&s_attention) break;
+					}
+					if (i != 0 || timeout <= jiffies) break;
+					sbp_sleep(0);
+					i = 1;
+				}
+				if (i==0) msg(DBG_AUD,"read_audio: STATUS TIMEOUT AFTER READ");
+				if (!(j&s_attention))
+				{
+					msg(DBG_AUD,"read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n");
+					i=cc_DriveReset();  /* ugly fix to prevent a hang */
+					continue;
+				}
+			}
+			do
+			{
+				if (fam0L_drive) cc_ReadStatus();
+				i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
+				if (i<0) { msg(DBG_AUD,
+					       "read_audio: cc_ReadStatus error after read: %02X\n",
+					       D_S[d].status_bits);
+					   continue; /* FIXME */
+				   }
+			}
+			while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
+			if (st_check)
+			{
+				i=cc_ReadError();
+				msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i);
+				continue;
+			}
+			memcpy_tofs((u_char *) read_audio.buf,
+				    (u_char *) D_S[d].aud_buf,
+				    read_audio.nframes*CD_FRAMESIZE_RAW);
+			msg(DBG_AUD,"read_audio: memcpy_tofs done.\n");
+			break;
+		}
+		cc_ModeSelect(CD_FRAMESIZE);
+		cc_ModeSense();
+		D_S[d].mode=READ_M1;
+		busy_audio=0;
+		if (data_tries == 0)
+		{
+			msg(DBG_AUD,"read_audio: failed after 5 tries.\n");
+			return (-8);
+		}
+		msg(DBG_AUD,"read_audio: successful return.\n");
+		return (0);
+	} /* end of CDROMREADAUDIO */
+		
+	case CDROMMULTISESSION: /* tell start-of-last-session */
+		msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n");
+		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_multisession));
+		if (st) return (st);
+		memcpy_fromfs(&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 (-EINVAL);
+		if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
+		else ms_info.xa_flag=0; /* invalid redirection address */
+		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
+		if (st) return (st);
+		memcpy_tofs((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 (0);
+		
+	case BLKRASET:
+		if(!suser())  return -EACCES;
+		if(!(inode->i_rdev)) return -EINVAL;
+		if(arg > 0xff) return -EINVAL;
+		read_ahead[MAJOR(inode->i_rdev)] = arg;
+		return (0);
+		
+	default:
+		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
+		return (-EINVAL);
+	} /* end switch(cmd) */
+}
+/*==========================================================================*/
+/*
+ *  Take care of the different block sizes between cdrom and Linux.
+ */
+static void sbp_transfer(void)
+{
+	long offs;
+	
+	while ( (CURRENT->nr_sectors > 0) &&
+	       (CURRENT->sector/4 >= D_S[d].sbp_first_frame) &&
+	       (CURRENT->sector/4 <= D_S[d].sbp_last_frame) )
+	{
+		offs = (CURRENT->sector - D_S[d].sbp_first_frame * 4) * 512;
+		memcpy(CURRENT->buffer, D_S[d].sbp_buf + offs, 512);
+		CURRENT->nr_sectors--;
+		CURRENT->sector++;
+		CURRENT->buffer += 512;
+	}
+}
+/*==========================================================================*/
+/*
+ *  I/O request routine, called from Linux kernel.
+ */
+static void DO_SBPCD_REQUEST(void)
+{
+	u_int block;
+	u_int nsect;
+	int i, status_tries, data_tries;
+	
+ request_loop:
+	INIT_REQUEST;
+	sti();
+	
+	if ((CURRENT == NULL) || CURRENT->rq_status == RQ_INACTIVE)
+		goto err_done;
+	if (CURRENT -> sector == -1)
+		goto err_done;
+	if (CURRENT->cmd != READ)
+	{
+		msg(DBG_INF, "bad cmd %d\n", CURRENT->cmd);
+		goto err_done;
+	}
+	i = MINOR(CURRENT->rq_dev);
+	if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
+	{
+		msg(DBG_INF, "do_request: bad device: %s\n",
+			kdevname(CURRENT->rq_dev));
+		goto err_done;
+	}
+	while (busy_audio) sbp_sleep(HZ); /* wait a bit */
+	busy_data=1;
+	
+	if (D_S[i].audio_state==audio_playing) goto err_done;
+	if (d!=i) switch_drive(i);
+	
+	block = CURRENT->sector; /* always numbered as 512-byte-pieces */
+	nsect = CURRENT->nr_sectors; /* always counted as 512-byte-pieces */
+	
+	msg(DBG_BSZ,"read sector %d (%d sectors)\n", block, nsect);
+#if 0
+	msg(DBG_MUL,"read LBA %d\n", block/4);
+#endif
+	
+	sbp_transfer();
+	/* if we satisfied the request from the buffer, we're done. */
+	if (CURRENT->nr_sectors == 0)
+	{
+		end_request(1);
+		goto request_loop;
+	}
+
+#if FUTURE
+	i=prepare(0,0); /* at moment not really a hassle check, but ... */
+	if (i!=0)
+		msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i);
+#endif FUTURE
+	
+	if (!st_spinning) cc_SpinUp();
+	
+	for (data_tries=n_retries; data_tries > 0; data_tries--)
+	{
+		for (status_tries=3; status_tries > 0; status_tries--)
+		{
+			flags_cmd_out |= f_respo3;
+			cc_ReadStatus();
+			if (sbp_status() != 0) break;
+			if (st_check) cc_ReadError();
+			sbp_sleep(1);    /* wait a bit, try again */
+		}
+		if (status_tries == 0)
+		{
+			msg(DBG_INF,"sbp_status: failed after 3 tries\n");
+			break;
+		}
+		
+		sbp_read_cmd();
+		sbp_sleep(0);
+		if (sbp_data() != 0)
+		{
+			end_request(1);
+			goto request_loop;
+		}
+	}
+	
+ err_done:
+	busy_data=0;
+	end_request(0);
+	sbp_sleep(0);    /* wait a bit, try again */
+	goto request_loop;
+}
+/*==========================================================================*/
+/*
+ *  build and send the READ command.
+ */
+static void sbp_read_cmd(void)
+{
+#undef OLD
+
+	int i;
+	int block;
+	
+	D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;      /* purge buffer */
+	D_S[d].sbp_current = 0;
+	block=CURRENT->sector/4;
+	if (block+D_S[d].sbp_bufsiz <= D_S[d].CDsize_frm)
+		D_S[d].sbp_read_frames = D_S[d].sbp_bufsiz;
+	else
+	{
+		D_S[d].sbp_read_frames=D_S[d].CDsize_frm-block;
+		/* avoid reading past end of data */
+		if (D_S[d].sbp_read_frames < 1)
+		{
+			msg(DBG_INF,"requested frame %d, CD size %d ???\n",
+			    block, D_S[d].CDsize_frm);
+			D_S[d].sbp_read_frames=1;
+		}
+	}
+	
+	flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
+	clr_cmdbuf();
+	if (fam0L_drive)
+	{
+		flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
+		if (D_S[d].xa_byte==0x20)
+		{
+			cmd_type=READ_M2;
+			drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
+			drvcmd[1]=(block>>16)&0x000000ff;
+			drvcmd[2]=(block>>8)&0x000000ff;
+			drvcmd[3]=block&0x000000ff;
+			drvcmd[5]=D_S[d].sbp_read_frames;
+		}
+		else
+		{
+			drvcmd[0]=CMD0_READ; /* "read frames", old drives */
+			if (D_S[d].drv_type>=drv_201)
+			{
+				lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
+				bin2bcdx(&drvcmd[1]);
+				bin2bcdx(&drvcmd[2]);
+				bin2bcdx(&drvcmd[3]);
+			}
+			else
+			{
+				drvcmd[1]=(block>>16)&0x000000ff;
+				drvcmd[2]=(block>>8)&0x000000ff;
+				drvcmd[3]=block&0x000000ff;
+			}
+			drvcmd[5]=D_S[d].sbp_read_frames;
+			drvcmd[6]=(D_S[d].drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
+		}
+	}
+	else if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_READ;
+		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+		drvcmd[6]=D_S[d].sbp_read_frames;
+	}
+	else if (fam2_drive)
+	{
+		drvcmd[0]=CMD2_READ;
+		lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+		drvcmd[5]=D_S[d].sbp_read_frames;
+		drvcmd[6]=0x02;
+	}
+	else if (famT_drive)
+	{
+		drvcmd[0]=CMDT_READ;
+		drvcmd[2]=(block>>24)&0x0ff;
+		drvcmd[3]=(block>>16)&0x0ff;
+		drvcmd[4]=(block>>8)&0x0ff;
+		drvcmd[5]=block&0x0ff;
+		drvcmd[7]=(D_S[d].sbp_read_frames>>8)&0x0ff;
+		drvcmd[8]=D_S[d].sbp_read_frames&0x0ff;
+	}
+#ifdef OLD
+	SBPCD_CLI;
+	for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+	if (famT_drive) for (i=7;i<10;i++) OUT(CDo_command,drvcmd[i]);
+	SBPCD_STI;
+#else
+	flags_cmd_out=f_putcmd;
+	response_count=0;
+	i=cmd_out(); /* immediate return here - read data "ourselves" */
+	if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i);
+#endif OLD
+	return;
+}
+/*==========================================================================*/
+/*
+ *  Check the completion of the read-data command.  On success, read
+ *  the D_S[d].sbp_bufsiz * 2048 bytes of data from the disk into buffer.
+ */
+static int sbp_data(void)
+{
+	int i=0, j=0, l, frame;
+	u_int try=0;
+	u_long timeout;
+	u_char *p;
+	u_int data_tries = 0;
+	u_int data_waits = 0;
+	u_int data_retrying = 0;
+	int error_flag;
+	int xa_count;
+	int max_latency;
+	int success;
+	int wait;
+	int duration;
+	
+	error_flag=0;
+	success=0;
+#if LONG_TIMING
+	max_latency=9*HZ;
+#else
+	if (D_S[d].f_multisession) max_latency=9*HZ;
+	else max_latency=3*HZ;
+#endif
+	msg(DBG_TE2,"beginning to READ\n");
+	duration=jiffies;
+	for (frame=0;frame<D_S[d].sbp_read_frames&&!error_flag; frame++)
+	{
+		SBPCD_CLI;
+		
+		del_timer(&data_timer);
+		data_timer.expires=jiffies+max_latency;
+		timed_out_data=0;
+		add_timer(&data_timer);
+		while (!timed_out_data) 
+		{
+			if (D_S[d].f_multisession) try=maxtim_data*4;
+			else try=maxtim_data;
+			msg(DBG_000,"sbp_data: CDi_status loop: try=%d.\n",try);
+			for ( ; try!=0;try--)
+			{
+				j=inb(CDi_status);
+				if (!(j&s_not_data_ready)) break;;
+				if (!(j&s_not_result_ready)) break;
+				if (fam0L_drive) if (j&s_attention) break;
+			}
+			if (!(j&s_not_data_ready)) goto data_ready;
+			if (try==0)
+			{
+				if (data_retrying == 0) data_waits++;
+				data_retrying = 1;
+				msg(DBG_000,"sbp_data: CDi_status loop: sleeping.\n");
+				sbp_sleep(1);
+				try = 1;
+			}
+		}
+		msg(DBG_INF,"sbp_data: CDi_status loop expired.\n");
+	data_ready:
+		del_timer(&data_timer);
+
+		if (timed_out_data)
+		{
+			msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j);
+			error_flag++;
+			break;
+		}
+		if (try==0)
+		{
+			msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j);
+			error_flag++;
+			break;
+		}
+		if (!(j&s_not_result_ready))
+		{
+			msg(DBG_INF, "sbp_data: RESULT_READY where DATA_READY awaited (%02X).\n", j);
+			response_count=20;
+			j=ResponseInfo();
+			j=inb(CDi_status);
+		}
+		if (j&s_not_data_ready)
+		{
+			if ((D_S[d].ored_ctl_adr&0x40)==0)
+				msg(DBG_INF, "CD contains no data tracks.\n");
+			else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j);
+			error_flag++;
+			break;
+		}
+		SBPCD_STI;
+		error_flag=0;
+		msg(DBG_000, "sbp_data: beginning to read.\n");
+		p = D_S[d].sbp_buf + frame *  CD_FRAMESIZE;
+		if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+		if (cmd_type==READ_M2) insb(CDi_data, xa_head_buf, CD_XA_HEAD);
+		insb(CDi_data, p, CD_FRAMESIZE);
+		if (cmd_type==READ_M2) insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
+		if (famT_drive)	msg(DBG_TE2, "================frame read=================.\n");
+		D_S[d].sbp_current++;
+		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+		if (cmd_type==READ_M2)
+		{
+			for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++)
+				sprintf(&msgbuf[xa_count*3], " %02X", xa_head_buf[xa_count]);
+			msgbuf[xa_count*3]=0;
+			msg(DBG_XA1,"xa head:%s\n", msgbuf);
+		}
+		data_retrying = 0;
+		data_tries++;
+		if (data_tries >= 1000)
+		{
+			msg(DBG_INF,"sbp_data() statistics: %d waits in %d frames.\n", data_waits, data_tries);
+			data_waits = data_tries = 0;
+		}
+	}
+	duration=jiffies-duration;
+	msg(DBG_TE2,"time to read %d frames: %d jiffies .\n",frame,duration);
+	if (famT_drive)
+	{
+		wait=8;
+		do
+		{
+			sbp_sleep(1);
+			OUT(CDo_sel_i_d,0);
+			i=inb(CDi_status);
+			if (!(i&s_not_data_ready))
+			{
+				OUT(CDo_sel_i_d,1);
+				j=0;
+				do
+				{
+					i=inb(CDi_data);
+					j++;
+					i=inb(CDi_status);
+				}
+				while (!(i&s_not_data_ready));
+				msg(DBG_TEA, "=============too much data (%d bytes)=================.\n", j);
+			}
+			if (!(i&s_not_result_ready))
+			{
+				OUT(CDo_sel_i_d,0);
+				l=0;
+				do
+				{
+					infobuf[l++]=inb(CDi_info);
+					i=inb(CDi_status);
+				}
+				while (!(i&s_not_result_ready));
+				if (infobuf[0]==0x00) success=1;
+#if 1
+				for (j=0;j<l;j++) sprintf(&msgbuf[j*3], " %02X", infobuf[j]);
+				msgbuf[j*3]=0;
+				msg(DBG_TE2,"sbp_data info response:%s\n", msgbuf);
+#endif
+				if (infobuf[0]==0x02)
+				{
+					error_flag++;
+					do
+					{
+						++recursion;
+						if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (sbp_data): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",recursion);
+						else msg(DBG_TEA,"sbp_data: CMDT_READ_ERR necessary.\n");
+						clr_cmdbuf();
+						drvcmd[0]=CMDT_READ_ERR;
+						j=cmd_out_T(); /* !!! recursive here !!! */
+						--recursion;
+						sbp_sleep(1);
+					}
+					while (j<0);
+					D_S[d].error_state=infobuf[2];
+					D_S[d].b3=infobuf[3];
+					D_S[d].b4=infobuf[4];
+				}
+				break;
+			}
+			else
+			{
+#if 0
+				msg(DBG_TEA, "============= waiting for result=================.\n");
+				sbp_sleep(1);
+#endif
+			}
+		}
+		while (wait--);
+	}
+
+	if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
+	{
+		msg(DBG_TEA, "================error flag: %d=================.\n", error_flag);
+		msg(DBG_INF,"sbp_data: read aborted by drive.\n");
+#if 1
+		i=cc_DriveReset(); /* ugly fix to prevent a hang */
+#else
+		i=cc_ReadError();
+#endif
+		return (0);
+	}
+	
+	if (fam0L_drive)
+	{
+		SBPCD_CLI;
+		i=maxtim_data;
+		for (timeout=jiffies+HZ; timeout > jiffies; timeout--)
+		{
+			for ( ;i!=0;i--)
+			{
+				j=inb(CDi_status);
+				if (!(j&s_not_data_ready)) break;
+				if (!(j&s_not_result_ready)) break;
+				if (j&s_attention) break;
+			}
+			if (i != 0 || timeout <= jiffies) break;
+			sbp_sleep(0);
+			i = 1;
+		}
+		if (i==0) msg(DBG_INF,"status timeout after READ.\n");
+		if (!(j&s_attention))
+		{
+			msg(DBG_INF,"sbp_data: timeout waiting DRV_ATTN - retrying.\n");
+			i=cc_DriveReset();  /* ugly fix to prevent a hang */
+			SBPCD_STI;
+			return (0);
+		}
+		SBPCD_STI;
+	}
+	
+#if 0
+	if (!success)
+#endif 0
+		do
+		{
+			if (fam0L_drive) cc_ReadStatus();
+#if 1
+			if (famT_drive) msg(DBG_TE2, "================before ResponseStatus=================.\n", i);
+#endif 1
+			i=ResponseStatus();  /* builds status_bits, returns orig. status (old) or faked p_success (new) */
+#if 1
+			if (famT_drive)	msg(DBG_TE2, "================ResponseStatus: %d=================.\n", i);
+#endif 1
+			if (i<0)
+			{
+				msg(DBG_INF,"bad cc_ReadStatus after read: %02X\n", D_S[d].status_bits);
+				return (0);
+			}
+		}
+		while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
+	if (st_check)
+	{
+		i=cc_ReadError();
+		msg(DBG_INF,"cc_ReadError was necessary after read: %d\n",i);
+		return (0);
+	}
+	if (fatal_err)
+	{
+		fatal_err=0;
+		D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;      /* purge buffer */
+		D_S[d].sbp_current = 0;
+		msg(DBG_INF,"sbp_data: fatal_err - retrying.\n");
+		return (0);
+	}
+	
+	D_S[d].sbp_first_frame = CURRENT -> sector / 4;
+	D_S[d].sbp_last_frame = D_S[d].sbp_first_frame + D_S[d].sbp_read_frames - 1;
+	sbp_transfer();
+#if 1
+	if (famT_drive)	msg(DBG_TE2, "================sbp_transfer() done=================.\n");
+#endif 1
+	return (1);
+}
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ *  Open the device special file.  Check that a disk is in. Read TOC.
+ */
+static int sbpcd_open(struct inode *ip, struct file *fp)
+{
+	int i;
+	
+	i = MINOR(ip->i_rdev);
+	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
+	{
+		msg(DBG_INF, "open: bad device: %04X\n", ip->i_rdev);
+		return (-ENXIO);             /* no such drive */
+	}
+	if (fp->f_mode & 2)
+		return -EROFS;
+	
+	switch_drive(i);
+	
+	i=cc_ReadError();
+	flags_cmd_out |= f_respo2;
+	cc_ReadStatus();                         /* command: give 1-byte status */
+	i=ResponseStatus();
+	if (famT_drive&&(i<0))
+	{
+		cc_DriveReset();
+		i=ResponseStatus();
+		i=ResponseStatus();
+	}
+	if (i<0)
+	{
+		msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
+		return (-EIO);                  /* drive doesn't respond */
+	}
+	if (famT_drive)	msg(DBG_TE2,"sbpcd_open: ResponseStatus=%02X\n", i);
+	if (!st_door_closed)
+	{
+		if (famT_drive)	msg(DBG_TE2,"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_TE2,"sbpcd_open: !st_spinning.\n");
+			cc_SpinUp();
+			flags_cmd_out |= f_respo2;
+			cc_ReadStatus();
+			i=ResponseStatus();
+		}
+	if (famT_drive)	msg(DBG_TE2,"sbpcd_open: status %02X\n", D_S[d].status_bits);
+	if (!st_door_closed||!st_caddy_in)
+	{
+		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
+		return (-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)
+	{
+		i=LockDoor();
+		D_S[d].open_count=1;
+		if (famT_drive)	msg(DBG_TE2,"sbpcd_open: before i=DiskInfo();.\n");
+		i=DiskInfo();
+		if (famT_drive)	msg(DBG_TE2,"sbpcd_open: after i=DiskInfo();.\n");
+		if ((D_S[d].ored_ctl_adr&0x40)==0)
+			msg(DBG_INF,"CD contains no data tracks.\n");
+	}
+	if (!st_spinning) cc_SpinUp();
+	return (0);
+}
+/*==========================================================================*/
+/*
+ *  On close, we flush all sbp blocks from the buffer cache.
+ */
+static void sbpcd_release(struct inode * ip, struct file * file)
+{
+	int i;
+	
+	i = MINOR(ip->i_rdev);
+	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
+	{
+		msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev);
+		return;
+	}
+	switch_drive(i);
+	
+	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);
+	
+	/*
+	 * 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 */
+	{
+		if (--D_S[d].open_count<=0) 
+		{
+			i=UnLockDoor();
+			if (D_S[d].audio_state!=audio_playing)
+				if (D_S[d].f_eject) cc_SpinDown();
+			D_S[d].diskstate_flags &= ~cd_size_bit;
+			D_S[d].open_count=0; 
+		}
+	}
+}
+/*==========================================================================*/
+/*
+ *
+ */
+static struct file_operations sbpcd_fops =
+{
+	NULL,                   /* lseek - default */
+	block_read,             /* read - general block-dev read */
+	block_write,            /* write - general block-dev write */
+	NULL,                   /* readdir - bad */
+	NULL,                   /* select */
+	sbpcd_ioctl,            /* ioctl */
+	NULL,                   /* mmap */
+	sbpcd_open,             /* open */
+	sbpcd_release,          /* release */
+	NULL,                   /* fsync */
+	NULL,                   /* fasync */
+	sbpcd_chk_disk_change,  /* media_change */
+	NULL                    /* revalidate */
+};
+/*==========================================================================*/
+/*
+ * accept "kernel command line" parameters 
+ * (suggested by Peter MacDonald with SLS 1.03)
+ *
+ * This is only implemented for the first controller. Should be enough to
+ * allow installing with a "strange" distribution kernel.
+ *
+ * use: tell LILO:
+ *                 sbpcd=0x230,SoundBlaster
+ *             or
+ *                 sbpcd=0x300,LaserMate
+ *             or
+ *                 sbpcd=0x330,SoundScape
+ *
+ * (upper/lower case sensitive here - but all-lowercase is ok!!!).
+ *
+ * the address value has to be the CDROM PORT ADDRESS -
+ * not the soundcard base address.
+ * For the SPEA/SoundScape setup, DO NOT specify the "configuration port"
+ * address, but the address which is really used for the CDROM (usually 8
+ * bytes above).
+ *
+ */
+#if (SBPCD_ISSUE-1)
+static
+#endif
+void sbpcd_setup(const char *s, int *p)
+{
+	setup_done++;
+	msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s);
+	sbpro_type=0; /* default: "LaserMate" */
+	if (p[0]>1) sbpro_type=p[2];
+	if (!strcmp(s,str_sb)) sbpro_type=1;
+	else if (!strcmp(s,str_sb_l)) sbpro_type=1;
+	else if (!strcmp(s,str_sp)) sbpro_type=2;
+	else if (!strcmp(s,str_sp_l)) sbpro_type=2;
+	else if (!strcmp(s,str_ss)) sbpro_type=2;
+	else if (!strcmp(s,str_ss_l)) sbpro_type=2;
+	if (p[0]>0) sbpcd_ioaddr=p[1];
+	
+	CDo_command=sbpcd_ioaddr;
+	CDi_info=sbpcd_ioaddr;
+	CDi_status=sbpcd_ioaddr+1;
+	CDo_sel_i_d=sbpcd_ioaddr+1;
+	CDo_reset=sbpcd_ioaddr+2;
+	CDo_enable=sbpcd_ioaddr+3; 
+	if (sbpro_type==1)
+	{
+		MIXER_addr=sbpcd_ioaddr-0x10+0x04;
+		MIXER_data=sbpcd_ioaddr-0x10+0x05;
+		CDi_data=sbpcd_ioaddr;
+	}
+	else CDi_data=sbpcd_ioaddr+2;
+}
+/*==========================================================================*/
+/*
+ * Sequoia S-1000 CD-ROM Interface Configuration
+ * as used within SPEA Media FX, Ensonic SoundScape and some Reveal cards
+ * The soundcard has to get jumpered for the interface type "Panasonic"
+ * (not Sony or Mitsumi) and to get soft-configured for
+ *     -> configuration port address
+ *     -> CDROM port offset (num_ports): has to be 8 here. Possibly this
+ *        offset value determines the interface type (none, Panasonic,
+ *        Mitsumi, Sony).
+ *        The interface uses a configuration port (0x320, 0x330, 0x340, 0x350)
+ *        some bytes below the real CDROM address.
+ *         
+ *        For the Panasonic style (LaserMate) interface and the configuration
+ *        port 0x330, we have to use an offset of 8; so, the real CDROM port
+ *        address is 0x338.
+ */
+static int config_spea(void)
+{
+	int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
+	/* base address offset between configuration port and CDROM port */
+	int irq_number=0; /* off:0x00, 2:0x01, 7:0x03, 12:0x05, 15:0x07 */
+	int dma_channel=0; /* off: 0x00, 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68 */
+	int dack_polarity=0; /* L:0x00, H:0x80 */
+	int drq_polarity=0x40; /* L:0x00, H:0x40 */
+	int i;
+
+#define SPEA_REG_1 sbpcd_ioaddr-0x08+4
+#define SPEA_REG_2 sbpcd_ioaddr-0x08+5
+	
+	OUT(SPEA_REG_1,0xFF);
+	i=inb(SPEA_REG_1);
+	if (i!=0x0F)
+	{
+		msg(DBG_SEQ,"no SPEA interface at %04X present.\n", sbpcd_ioaddr);
+		return (-1); /* no interface found */
+	}
+	OUT(SPEA_REG_1,0x04);
+	OUT(SPEA_REG_2,0xC0);
+	
+	OUT(SPEA_REG_1,0x05);
+	OUT(SPEA_REG_2,0x10|drq_polarity|dack_polarity);
+	
+#if 1
+#define SPEA_PATTERN 0x80
+#else
+#define SPEA_PATTERN 0x00
+#endif
+	OUT(SPEA_REG_1,0x06);
+	OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
+	OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
+	
+	OUT(SPEA_REG_1,0x09);
+	i=(inb(SPEA_REG_2)&0xCF)|n_ports;
+	OUT(SPEA_REG_2,i);
+	
+	sbpro_type = 0; /* acts like a LaserMate interface now */
+	msg(DBG_SEQ,"found SoundScape interface at %04X.\n", sbpcd_ioaddr);
+	return (0);
+}
+/*==========================================================================*/
+/*
+ *  Test for presence of drive and initialize it.  Called at boot time.
+ */
+#ifdef MODULE
+int init_module(void)
+#else
+int SBPCD_INIT(void)
+#endif MODULE
+{
+	int i=0, j=0;
+	int addr[2]={1, CDROM_PORT};
+	int port_index;
+	
+	sti();
+	
+	msg(DBG_INF,"sbpcd.c %s\n", VERSION);
+#ifndef MODULE
+#if DISTRIBUTION
+	if (!setup_done)
+	{
+		msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, Longshine, TEAC CD-ROM drives\n");
+		msg(DBG_INF,"= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
+		msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an NE2000 card).\n");
+		msg(DBG_INF,"If that happens, you have to reboot and use the\n");
+		msg(DBG_INF,"LILO (kernel) command line feature like:\n");
+		msg(DBG_INF,"   LILO boot: ... sbpcd=0x230,SoundBlaster\n");
+		msg(DBG_INF,"or like:\n");
+		msg(DBG_INF,"   LILO boot: ... sbpcd=0x300,LaserMate\n");
+		msg(DBG_INF,"or like:\n");
+		msg(DBG_INF,"   LILO boot: ... sbpcd=0x338,SoundScape\n");
+		msg(DBG_INF,"with your REAL address.\n");
+		msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = == = = =\n");
+	}
+#endif DISTRIBUTION
+	sbpcd[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
+	sbpcd[1]=sbpro_type; /* possibly changed by kernel command line */
+#endif MODULE
+	
+	for (port_index=0;port_index<NUM_PROBE;port_index+=2)
+	{
+		addr[1]=sbpcd[port_index];
+		if (addr[1]==0) break;
+		if (check_region(addr[1],4))
+		{
+			msg(DBG_INF,"check_region: %03X is not free.\n",addr[1]);
+			continue;
+		}
+		if (sbpcd[port_index+1]==2) type=str_sp;
+		else if (sbpcd[port_index+1]==1) type=str_sb;
+		else type=str_lm;
+		sbpcd_setup(type, addr);
+#if DISTRIBUTION
+		msg(DBG_INF,"Scanning 0x%X (%s)...\n", CDo_command, type);
+#endif DISTRIBUTION
+		if (sbpcd[port_index+1]==2)
+		{
+			i=config_spea();
+			if (i<0) continue;
+		}
+#ifdef PATH_CHECK
+		if (check_card(addr[1])) continue;
+#endif PATH_CHECK
+		i=check_drives();
+		msg(DBG_INI,"check_drives done.\n");
+		if (i>=0) break; /* drive found */
+	} /* end of cycling through the set of possible I/O port addresses */
+	
+	if (ndrives==0)
+	{
+		msg(DBG_INF, "No drive found.\n");
+#ifdef MODULE
+		return -EIO;
+#else
+		goto init_done;
+#endif MODULE
+	}
+	
+	if (port_index>0)
+		msg(DBG_INF, "You should configure sbpcd.h for your hardware.\n");
+	check_datarate();
+	msg(DBG_INI,"check_datarate done.\n");
+	
+#if 0
+	if (!famL_drive)
+	{
+		OUT(CDo_reset,0);
+		sbp_sleep(HZ);
+	}
+#endif 0
+
+	for (j=0;j<NR_SBPCD;j++)
+	{
+		if (D_S[j].drv_id==-1) continue;
+		switch_drive(j);
+#if 1
+		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;
+#if EJECT
+		if (!fam0_drive) D_S[d].f_eject=1;
+		else D_S[d].f_eject=0;
+#else
+		D_S[d].f_eject=0;
+#endif
+		
+		cc_ReadStatus();
+		i=ResponseStatus();  /* returns orig. status or p_busy_new */
+		if (famT_drive) i=ResponseStatus();  /* returns orig. status or p_busy_new */
+		if (i<0)
+			if (i!=-402)
+				msg(DBG_INF,"init: ResponseStatus returns %d.\n",i);
+		else
+		{
+			if (st_check)
+			{
+				i=cc_ReadError();
+				msg(DBG_INI,"init: cc_ReadError returns %d\n",i);
+			}
+		}
+		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)
+		{
+			timeout=jiffies+2*HZ;
+			do
+			{
+				i=GetStatus();
+				msg(DBG_INI,"init: second GetStatus: %02X\n",i);
+				msg(DBG_LCS,
+				    "init: second GetStatus: error_byte=%d\n",
+				    D_S[d].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;
+	}
+	
+	/*
+	 * Turn on the CD audio channels.
+	 * For "compatible" soundcards (with "SBPRO 0" or "SBPRO 2"), the addresses
+	 * are obtained from SOUND_BASE (see sbpcd.h).
+	 */
+	if ((sbpro_type==1) || (SOUND_BASE))
+	{
+		if (sbpro_type!=1)
+		{
+			MIXER_addr=SOUND_BASE+0x04; /* sound card's address register */
+			MIXER_data=SOUND_BASE+0x05; /* sound card's data register */
+		}
+		OUT(MIXER_addr,MIXER_CD_Volume); /* select SB Pro mixer register */
+		OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
+	}
+	
+	if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0)
+	{
+		msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
+#ifdef MODULE
+		return -EIO;
+#else
+		goto init_done;
+#endif MODULE
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
+	
+	request_region(CDo_command,4,major_name);
+	
+	for (j=0;j<NR_SBPCD;j++)
+	{
+		if (D_S[j].drv_id==-1) continue;
+		switch_drive(j);
+		/*
+		 * allocate memory for the frame buffers
+		 */
+		D_S[j].aud_buf=NULL;
+		D_S[j].sbp_audsiz=0;
+		D_S[j].sbp_bufsiz=SBP_BUFFER_FRAMES;
+		if (D_S[j].drv_type&drv_fam1)
+			if (READ_AUDIO>0) D_S[j].sbp_audsiz=READ_AUDIO;
+		D_S[j].sbp_buf=(u_char *) vmalloc(D_S[j].sbp_bufsiz*CD_FRAMESIZE);
+		if (D_S[j].sbp_buf==NULL)
+		{
+			msg(DBG_INF,"data buffer (%d frames) not available.\n",D_S[j].sbp_bufsiz);
+			return -EIO;
+		}
+		msg(DBG_INF,"data buffer size: %d frames.\n",SBP_BUFFER_FRAMES);
+		if (D_S[j].sbp_audsiz>0)
+		{
+			D_S[j].aud_buf=(u_char *) vmalloc(D_S[j].sbp_audsiz*CD_FRAMESIZE_RAW);
+			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);
+		}
+		/*
+		 * set the block size
+		 */
+		sbpcd_blocksizes[j]=CD_FRAMESIZE;
+	}
+	blksize_size[MAJOR_NR]=sbpcd_blocksizes;
+	
+#ifdef MODULE
+	return (0);
+#else
+ init_done:
+#if !(SBPCD_ISSUE-1)
+#ifdef CONFIG_SBPCD2
+	sbpcd2_init();
+#endif
+#ifdef CONFIG_SBPCD3
+	sbpcd3_init();
+#endif
+#ifdef CONFIG_SBPCD4
+	sbpcd4_init();
+#endif
+#endif
+	return 0;
+#endif MODULE
+}
+/*==========================================================================*/
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int j;
+	
+	if (MOD_IN_USE)
+	{
+		msg(DBG_INF, "%s module in use - can't remove it.\n", major_name);
+		return;
+	}
+	if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
+	{
+		msg(DBG_INF, "What's that: can't unregister %s.\n", major_name);
+		return;
+	}
+	release_region(CDo_command,4);
+	
+	for (j=0;j<NR_SBPCD;j++)
+	{
+		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);
+	}
+	msg(DBG_INF, "%s module released.\n", major_name);
+}
+#endif MODULE
+/*==========================================================================*/
+/*
+ * Check if the media has changed in the CD-ROM drive.
+ * used externally (isofs/inode.c, fs/buffer.c)
+ * Currently disabled (has to get "synchronized").
+ */
+static int sbpcd_chk_disk_change(kdev_t full_dev)
+{
+	int i, st;
+	
+	msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev));
+	return (0); /* "busy" test necessary before we really can check */
+	
+	i=MINOR(full_dev);
+	if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) )
+	{
+		msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev);
+		return (-1);
+	}
+	
+	switch_drive(i);
+	
+	cc_ReadStatus();                         /* command: give 1-byte status */
+	st=ResponseStatus();
+	msg(DBG_CHK,"media_check: %02X\n",D_S[d].status_bits);
+	if (st<0)
+	{
+		msg(DBG_INF,"media_check: ResponseStatus error.\n");
+		return (1); /* status not obtainable */
+	}
+	if (D_S[d].CD_changed==0xFF) msg(DBG_CHK,"media_check: \"changed\" assumed.\n");
+	if (!st_spinning) msg(DBG_CHK,"media_check: motor off.\n");
+	if (!st_door_closed)
+	{
+		msg(DBG_CHK,"media_check: door open.\n");
+		D_S[d].CD_changed=0xFF;
+	}
+	if (!st_caddy_in)
+	{
+		msg(DBG_CHK,"media_check: no disk in drive.\n");
+		D_S[d].open_count=0;
+		D_S[d].CD_changed=0xFF;
+	}
+	if (!st_diskok) msg(DBG_CHK,"media_check: !st_diskok.\n");
+	
+#if 0000
+	if (D_S[d].CD_changed==0xFF)
+	{
+		D_S[d].CD_changed=1;
+		return (1); /* driver had a change detected before */
+	}
+#endif 0000 /* seems to give additional errors at the moment */
+	
+	if (!st_diskok) return (1); /* disk not o.k. */
+	if (!st_caddy_in) return (1); /* disk removed */
+	if (!st_door_closed) return (1); /* door open */
+	return (0);
+}
+/*==========================================================================*/
+/*
+ * 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 v1.3.35/linux/drivers/cdrom/sbpcd2.c linux/drivers/cdrom/sbpcd2.c
--- v1.3.35/linux/drivers/cdrom/sbpcd2.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/sbpcd2.c	Fri Jan 13 09:02:32 1995
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 2
+#include "sbpcd.c"
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sbpcd3.c linux/drivers/cdrom/sbpcd3.c
--- v1.3.35/linux/drivers/cdrom/sbpcd3.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/sbpcd3.c	Fri Jan 13 09:02:32 1995
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 3
+#include "sbpcd.c"
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sbpcd4.c linux/drivers/cdrom/sbpcd4.c
--- v1.3.35/linux/drivers/cdrom/sbpcd4.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/sbpcd4.c	Fri Jan 13 09:02:32 1995
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 4
+#include "sbpcd.c"
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sjcd.c linux/drivers/cdrom/sjcd.c
--- v1.3.35/linux/drivers/cdrom/sjcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/sjcd.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,1915 @@
+/* -- sjcd.c
+ *
+ *   Sanyo CD-ROM device driver implementation, Version 1.5
+ *   Copyright (C) 1995  Vadim V. Model
+ *
+ *   model@cecmow.enet.dec.com
+ *   vadim@rbrf.ru
+ *   vadim@ipsun.ras.ru
+ *
+ *   ISP16 detection and configuration.
+ *   Copyright (C) 1995  Eric van der Maarel (maarel@marin.nl)
+ *                   and Vadim Model (vadim@cecmow.enet.dec.com)
+ *
+ *
+ *  This driver is based on pre-works by Eberhard Moenkeberg (emoenke@gwdg.de);
+ *  it was developed under use of mcd.c from Martin Harriss, with help of
+ *  Eric van der Maarel (maarel@marin.nl).
+ *
+ *  ISP16 detection and configuration by Eric van der Maarel (maarel@marin.nl).
+ *  Sound configuration by Vadim V. Model (model@cecmow.enet.dec.com)
+ *
+ *  It is planned to include these routines into sbpcd.c later - to make
+ *  a "mixed use" on one cable possible for all kinds of drives which use
+ *  the SoundBlaster/Panasonic style CDROM interface. But today, the
+ *  ability to install directly from CDROM is more important than flexibility.
+ *
+ *  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:
+ *  1.1 First public release with kernel version 1.3.7.
+ *      Written by Vadim Model.
+ *  1.2 Added detection and configuration of cdrom interface
+ *      on ISP16 soundcard.
+ *      Allow for command line options: sjcd=<io_base>,<irq>,<dma>
+ *  1.3 Some minor changes to README.sjcd.
+ *  1.4 MSS Sound support!! Listen to a CD through the speakers.
+ *  1.5 Module support and bugfixes.
+ *      Tray locking.
+ *
+ */
+
+#include <linux/major.h>
+#include <linux/config.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#define sjcd_init init_module
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[]= UTS_RELEASE;
+#endif
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR SANYO_CDROM_MAJOR
+#include <linux/blk.h>
+#include <linux/sjcd.h>
+
+/* 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 due to soundcard conflicts */
+#define ISP16_IRQ_7  0x08  /* shouldn't be used due to soundcard 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 */
+
+static short isp16_detect(void);
+static short isp16_c928__detect(void);
+static short isp16_c929__detect(void);
+static short isp16_cdi_config( int base, u_char drive_type, int irq, int dma );
+static void isp16_sound_config( void );
+static short isp16_type; /* dependent on type of interface card */
+static u_char isp16_ctrl;
+static u_short isp16_enable_port;
+
+static int sjcd_present = 0;
+static u_char special_mask = 0;
+
+static unsigned char defaults[ 16 ] = {
+  0xA8, 0xA8, 0x18, 0x18, 0x18, 0x18, 0x8E, 0x8E,
+  0x03, 0x00, 0x02, 0x00, 0x0A, 0x00, 0x00, 0x00
+};
+
+#define SJCD_BUF_SIZ 32 /* cdr-h94a has internal 64K buffer */
+
+/*
+ * buffer for block size conversion
+ */
+static char sjcd_buf[ 2048 * SJCD_BUF_SIZ ];
+static volatile int sjcd_buf_bn[ SJCD_BUF_SIZ ], sjcd_next_bn;
+static volatile int sjcd_buf_in, sjcd_buf_out = -1;
+
+/*
+ * Status.
+ */
+static unsigned short sjcd_status_valid = 0;
+static unsigned short sjcd_door_closed;
+static unsigned short sjcd_door_was_open;
+static unsigned short sjcd_media_is_available;
+static unsigned short sjcd_media_is_changed;
+static unsigned short sjcd_toc_uptodate = 0;
+static unsigned short sjcd_command_failed;
+static volatile unsigned char sjcd_completion_status = 0;
+static volatile unsigned char sjcd_completion_error = 0;
+static unsigned short sjcd_command_is_in_progress = 0;
+static unsigned short sjcd_error_reported = 0;
+
+static int sjcd_open_count;
+
+static int sjcd_audio_status;
+static struct sjcd_play_msf sjcd_playing;
+
+static int sjcd_port = SJCD_BASE_ADDR;
+static int sjcd_irq  = SJCD_INTR_NR;
+static int sjcd_dma  = SJCD_DMA_NR;
+
+static struct wait_queue *sjcd_waitq = NULL;
+
+/*
+ * Data transfer.
+ */
+static volatile unsigned short sjcd_transfer_is_active = 0;
+
+enum sjcd_transfer_state {
+  SJCD_S_IDLE     = 0,
+  SJCD_S_START    = 1,
+  SJCD_S_MODE     = 2,
+  SJCD_S_READ     = 3,
+  SJCD_S_DATA     = 4,
+  SJCD_S_STOP     = 5,
+  SJCD_S_STOPPING = 6
+};
+static enum sjcd_transfer_state sjcd_transfer_state = SJCD_S_IDLE;
+static long sjcd_transfer_timeout = 0;
+static int sjcd_read_count = 0;
+static unsigned char sjcd_mode = 0;
+
+#define SJCD_READ_TIMEOUT 5000
+
+#if defined( SJCD_GATHER_STAT )
+/*
+ * Statistic.
+ */
+static struct sjcd_stat statistic;
+#endif
+
+/*
+ * Timer.
+ */
+static struct timer_list sjcd_delay_timer = { NULL, NULL, 0, 0, NULL };
+
+#define SJCD_SET_TIMER( func, tmout )           \
+    ( sjcd_delay_timer.expires = jiffies+tmout,         \
+      sjcd_delay_timer.function = ( void * )func, \
+      add_timer( &sjcd_delay_timer ) )
+
+#define CLEAR_TIMER del_timer( &sjcd_delay_timer )
+
+/*
+ * Set up device, i.e., use command line data to set
+ * base address, irq and dma.
+ */
+void sjcd_setup( char *str, int *ints )
+{
+   if (ints[0] > 0)
+      sjcd_port = ints[1];
+   if (ints[0] > 1)      
+      sjcd_irq = ints[2];
+   if (ints[0] > 2)      
+      sjcd_dma = ints[3];
+}
+
+/*
+ * Special converters.
+ */
+static unsigned char bin2bcd( int bin ){
+  int u, v;
+
+  u = bin % 10; v = bin / 10;
+  return( u | ( v << 4 ) );
+}
+
+static int bcd2bin( unsigned char bcd ){
+    return( ( bcd >> 4 ) * 10 + ( bcd & 0x0F ) );
+}
+
+static long msf2hsg( struct msf *mp ){
+  return( bcd2bin( mp->frame ) + bcd2bin( mp->sec ) * 75
+	 + bcd2bin( mp->min ) * 4500 - 150 );
+}
+
+static void hsg2msf( long hsg, struct msf *msf ){
+  hsg += 150; msf->min = hsg / 4500;
+  hsg %= 4500; msf->sec = hsg / 75; msf->frame = hsg % 75;
+  msf->min = bin2bcd( msf->min );       /* convert to BCD */
+  msf->sec = bin2bcd( msf->sec );
+  msf->frame = bin2bcd( msf->frame );
+}
+
+/*
+ * Send a command to cdrom. Invalidate status.
+ */
+static void sjcd_send_cmd( unsigned char cmd ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: send_cmd( 0x%x )\n", cmd );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Send a command with one arg to cdrom. Invalidate status.
+ */
+static void sjcd_send_1_cmd( unsigned char cmd, unsigned char a ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: send_1_cmd( 0x%x, 0x%x )\n", cmd, a );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  outb( a, SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Send a command with four args to cdrom. Invalidate status.
+ */
+static void sjcd_send_4_cmd( unsigned char cmd, unsigned char a,
+	    unsigned char b, unsigned char c, unsigned char d ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: send_4_cmd( 0x%x )\n", cmd );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  outb( a, SJCDPORT( 0 ) );
+  outb( b, SJCDPORT( 0 ) );
+  outb( c, SJCDPORT( 0 ) );
+  outb( d, SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Send a play or read command to cdrom. Invalidate Status.
+ */
+static void sjcd_send_6_cmd( unsigned char cmd, struct sjcd_play_msf *pms ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: send_long_cmd( 0x%x )\n", cmd );
+#endif
+  outb( cmd, SJCDPORT( 0 ) );
+  outb( pms->start.min,   SJCDPORT( 0 ) );
+  outb( pms->start.sec,   SJCDPORT( 0 ) );
+  outb( pms->start.frame, SJCDPORT( 0 ) );
+  outb( pms->end.min,     SJCDPORT( 0 ) );
+  outb( pms->end.sec,     SJCDPORT( 0 ) );
+  outb( pms->end.frame,   SJCDPORT( 0 ) );
+  sjcd_command_is_in_progress = 1;
+  sjcd_status_valid = 0;
+  sjcd_command_failed = 0;
+}
+
+/*
+ * Get a value from the data port. Should not block, so we use a little
+ * wait for a while. Returns 0 if OK.
+ */
+static int sjcd_load_response( void *buf, int len ){
+  unsigned char *resp = ( unsigned char * )buf;
+
+  for( ; len; --len ){ 
+    int i;
+    for( i = 200; i-- && !SJCD_STATUS_AVAILABLE( inb( SJCDPORT( 1 ) ) ); );
+    if( i > 0 ) *resp++ = ( unsigned char )inb( SJCDPORT( 0 ) );
+    else break;
+  }
+  return( len );
+}
+
+/*
+ * Load and parse command completion status (drive info byte and maybe error).
+ * Sorry, no error classification yet.
+ */
+static void sjcd_load_status( void ){
+  sjcd_media_is_changed = 0;
+  sjcd_completion_error = 0;
+  sjcd_completion_status = inb( SJCDPORT( 0 ) );
+  if( sjcd_completion_status & SST_DOOR_OPENED ){
+    sjcd_door_closed = sjcd_media_is_available = 0;
+  } else {
+    sjcd_door_closed = 1;
+    if( sjcd_completion_status & SST_MEDIA_CHANGED )
+      sjcd_media_is_available = sjcd_media_is_changed = 1;
+    else if( sjcd_completion_status & 0x0F ){
+      /*
+       * OK, we seem to catch an error ...
+       */
+      while( !SJCD_STATUS_AVAILABLE( inb( SJCDPORT( 1 ) ) ) );
+      sjcd_completion_error = inb( SJCDPORT( 0 ) );
+      if( ( sjcd_completion_status & 0x08 ) &&
+	 ( sjcd_completion_error & 0x40 ) )
+	sjcd_media_is_available = 0;
+      else sjcd_command_failed = 1;
+    } else sjcd_media_is_available = 1;
+  }
+  /*
+   * Ok, status loaded successfully.
+   */
+  sjcd_status_valid = 1, sjcd_error_reported = 0;
+  sjcd_command_is_in_progress = 0;
+
+  /*
+   * If the disk is changed, the TOC is not valid.
+   */
+  if( sjcd_media_is_changed ) sjcd_toc_uptodate = 0;
+#if defined( SJCD_TRACE )
+  printk( "sjcd: status %02x.%02x loaded.\n",
+	 ( int )sjcd_completion_status, ( int )sjcd_completion_error );
+#endif
+}
+
+/*
+ * Read status from cdrom. Check to see if the status is available.
+ */
+static int sjcd_check_status( void ){
+  /*
+   * Try to load the response from cdrom into buffer.
+   */
+  if( SJCD_STATUS_AVAILABLE( inb( SJCDPORT( 1 ) ) ) ){
+    sjcd_load_status();
+    return( 1 );
+  } else {
+    /*
+     * No status is available.
+     */
+    return( 0 );
+  }
+}
+
+/*
+ * This is just timout counter, and nothing more. Surprized ? :-)
+ */
+static volatile long sjcd_status_timeout;
+
+/*
+ * We need about 10 seconds to wait. The longest command takes about 5 seconds
+ * to probe the disk (usually after tray closed or drive reset). Other values
+ * should be thought of for other commands.
+ */
+#define SJCD_WAIT_FOR_STATUS_TIMEOUT 1000
+
+static void sjcd_status_timer( void ){
+  if( sjcd_check_status() ){
+    /*
+     * The command completed and status is loaded, stop waiting.
+     */
+    wake_up( &sjcd_waitq );
+  } else if( --sjcd_status_timeout <= 0 ){
+    /*
+     * We are timed out. 
+     */
+    wake_up( &sjcd_waitq );
+  } else {
+    /*
+     * We have still some time to wait. Try again.
+     */
+    SJCD_SET_TIMER( sjcd_status_timer, 1 );
+  }
+}
+
+/*
+ * Wait for status for 10 sec approx. Returns non-positive when timed out.
+ * Should not be used while reading data CDs.
+ */
+static int sjcd_wait_for_status( void ){
+  sjcd_status_timeout = SJCD_WAIT_FOR_STATUS_TIMEOUT;
+  SJCD_SET_TIMER( sjcd_status_timer, 1 ); 
+  sleep_on( &sjcd_waitq );    
+#if defined( SJCD_DIAGNOSTIC ) || defined ( SJCD_TRACE )
+  if( sjcd_status_timeout <= 0 )
+    printk( "sjcd: Error Wait For Status.\n" );
+#endif
+  return( sjcd_status_timeout );
+}
+
+static int sjcd_receive_status( void ){
+  int i;
+#if defined( SJCD_TRACE )
+  printk( "sjcd: receive_status\n" );
+#endif
+  /*
+   * Wait a bit for status available.
+   */
+  for( i = 200; i-- && ( sjcd_check_status() == 0 ); );
+  if( i < 0 ){
+#if defined( SJCD_TRACE )
+    printk( "sjcd: long wait for status\n" );
+#endif
+    if( sjcd_wait_for_status() <= 0 )
+      printk( "sjcd: Timeout when read status.\n" );
+    else i = 0;
+  }
+  return( i );
+}
+
+/*
+ * Load the status. Issue get status command and wait for status available.
+ */
+static void sjcd_get_status( void ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: get_status\n" );
+#endif
+  sjcd_send_cmd( SCMD_GET_STATUS );
+  sjcd_receive_status();
+}
+
+/*
+ * Check the drive if the disk is changed. Should be revised.
+ */
+static int sjcd_disk_change( kdev_t full_dev ){
+#if 0
+  printk( "sjcd_disk_change( 0x%x )\n", full_dev );
+#endif
+  if( MINOR( full_dev ) > 0 ){
+    printk( "sjcd: request error: invalid device minor.\n" );
+    return 0;
+  }
+  if( !sjcd_command_is_in_progress )
+    sjcd_get_status();
+  return( sjcd_status_valid ? sjcd_media_is_changed : 0 );
+}
+
+/*
+ * Read the table of contents (TOC) and TOC header if necessary.
+ * We assume that the drive contains no more than 99 toc entries.
+ */
+static struct sjcd_hw_disk_info sjcd_table_of_contents[ SJCD_MAX_TRACKS ];
+static unsigned char sjcd_first_track_no, sjcd_last_track_no;
+#define sjcd_disk_length  sjcd_table_of_contents[0].un.track_msf
+
+static int sjcd_update_toc( void ){
+  struct sjcd_hw_disk_info info;
+  int i;
+#if defined( SJCD_TRACE )
+  printk( "sjcd: update toc:\n" );
+#endif
+  /*
+   * check to see if we need to do anything
+   */
+  if( sjcd_toc_uptodate ) return( 0 );
+
+  /*
+   * Get the TOC start information.
+   */
+  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_1_TRACK );
+  sjcd_receive_status();
+
+  if( !sjcd_status_valid ){
+    printk( "cannot load status.\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_media_is_available ){
+    printk( "no disk in drive\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_command_failed ){
+    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
+      printk( "cannot load response about TOC start.\n" );
+      return( -1 );
+    }
+    sjcd_first_track_no = bcd2bin( info.un.track_no );
+  } else {
+    printk( "get first failed\n" );
+    return( -1 );
+  }
+#if defined( SJCD_TRACE )
+  printk( "TOC start 0x%02x ", sjcd_first_track_no );
+#endif
+  /*
+   * Get the TOC finish information.
+   */
+  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_L_TRACK );
+  sjcd_receive_status();
+
+  if( !sjcd_status_valid ){
+    printk( "cannot load status.\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_media_is_available ){
+    printk( "no disk in drive\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_command_failed ){
+    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
+      printk( "cannot load response about TOC finish.\n" );
+      return( -1 );
+    }
+    sjcd_last_track_no = bcd2bin( info.un.track_no );
+  } else {
+    printk( "get last failed\n" );
+    return( -1 );
+  }
+#if defined( SJCD_TRACE )
+  printk( "TOC finish 0x%02x ", sjcd_last_track_no );
+#endif
+  for( i = sjcd_first_track_no; i <= sjcd_last_track_no; i++ ){
+    /*
+     * Get the first track information.
+     */
+    sjcd_send_1_cmd( SCMD_GET_DISK_INFO, bin2bcd( i ) );
+    sjcd_receive_status();
+
+    if( !sjcd_status_valid ){
+      printk( "cannot load status.\n" );
+      return( -1 );
+    }
+
+    if( !sjcd_media_is_available ){
+      printk( "no disk in drive\n" );
+      return( -1 );
+    }
+
+    if( !sjcd_command_failed ){
+      if( sjcd_load_response( &sjcd_table_of_contents[ i ],
+			     sizeof( struct sjcd_hw_disk_info ) ) != 0 ){
+	printk( "cannot load info for %d track\n", i );
+	return( -1 );
+      }
+    } else {
+      printk( "get info %d failed\n", i );
+      return( -1 );
+    }
+  }
+
+  /*
+   * Get the disk lenght info.
+   */
+  sjcd_send_1_cmd( SCMD_GET_DISK_INFO, SCMD_GET_D_SIZE );
+  sjcd_receive_status();
+
+  if( !sjcd_status_valid ){
+    printk( "cannot load status.\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_media_is_available ){
+    printk( "no disk in drive\n" );
+    return( -1 );
+  }
+
+  if( !sjcd_command_failed ){
+    if( sjcd_load_response( &info, sizeof( info ) ) != 0 ){
+      printk( "cannot load response about disk size.\n" );
+      return( -1 );
+    }
+    sjcd_disk_length.min = info.un.track_msf.min;
+    sjcd_disk_length.sec = info.un.track_msf.sec;
+    sjcd_disk_length.frame = info.un.track_msf.frame;
+  } else {
+    printk( "get size failed\n" );
+    return( 1 );
+  }
+#if defined( SJCD_TRACE )
+  printk( "(%02x:%02x.%02x)\n", sjcd_disk_length.min,
+	 sjcd_disk_length.sec, sjcd_disk_length.frame );
+#endif
+  return( 0 );
+}
+
+/*
+ * Load subchannel information.
+ */
+static int sjcd_get_q_info( struct sjcd_hw_qinfo *qp ){
+  int s;
+#if defined( SJCD_TRACE )
+  printk( "sjcd: load sub q\n" );
+#endif
+  sjcd_send_cmd( SCMD_GET_QINFO );
+  s = sjcd_receive_status();
+  if( s < 0 || sjcd_command_failed || !sjcd_status_valid ){
+    sjcd_send_cmd( 0xF2 );
+    s = sjcd_receive_status();
+    if( s < 0 || sjcd_command_failed || !sjcd_status_valid ) return( -1 );
+    sjcd_send_cmd( SCMD_GET_QINFO );
+    s = sjcd_receive_status();
+    if( s < 0 || sjcd_command_failed || !sjcd_status_valid ) return( -1 );
+  }
+  if( sjcd_media_is_available )
+    if( sjcd_load_response( qp, sizeof( *qp ) ) == 0 ) return( 0 );
+  return( -1 );
+}
+
+/*
+ * Start playing from the specified position.
+ */
+static int sjcd_play( struct sjcd_play_msf *mp ){
+  struct sjcd_play_msf msf;
+
+  /*
+   * Turn the device to play mode.
+   */
+  sjcd_send_1_cmd( SCMD_SET_MODE, SCMD_MODE_PLAY );
+  if( sjcd_receive_status() < 0 ) return( -1 );
+
+  /*
+   * Seek to the starting point.
+   */
+  msf.start = mp->start;
+  msf.end.min = msf.end.sec = msf.end.frame = 0x00;
+  sjcd_send_6_cmd( SCMD_SEEK, &msf );
+  if( sjcd_receive_status() < 0 ) return( -1 );
+
+  /*
+   * Start playing.
+   */
+  sjcd_send_6_cmd( SCMD_PLAY, mp );
+  return( sjcd_receive_status() );
+}
+
+/*
+ * Tray control functions.
+ */
+static int sjcd_tray_close( void ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: tray_close\n" );
+#endif
+  sjcd_send_cmd( SCMD_CLOSE_TRAY );
+  return( sjcd_receive_status() );
+}
+
+static int sjcd_tray_lock( void ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: tray_lock\n" );
+#endif
+  sjcd_send_cmd( SCMD_LOCK_TRAY );
+  return( sjcd_receive_status() );
+}
+
+static int sjcd_tray_unlock( void ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: tray_unlock\n" );
+#endif
+  sjcd_send_cmd( SCMD_UNLOCK_TRAY );
+  return( sjcd_receive_status() );
+}
+
+static int sjcd_tray_open( void ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: tray_open\n" );
+#endif
+  sjcd_send_cmd( SCMD_EJECT_TRAY );
+  return( sjcd_receive_status() );
+}
+
+/*
+ * Do some user commands.
+ */
+static int sjcd_ioctl( struct inode *ip, struct file *fp,
+		       unsigned int cmd, unsigned long arg ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd:ioctl\n" );
+#endif
+
+  if( ip == NULL ) return( -EINVAL );
+
+  sjcd_get_status();
+  if( !sjcd_status_valid ) return( -EIO );
+  if( sjcd_update_toc() < 0 ) return( -EIO );
+
+  switch( cmd ){
+  case CDROMSTART:{
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: start\n" );
+#endif
+    return( 0 );
+  }
+
+  case CDROMSTOP:{
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: stop\n" );
+#endif
+    sjcd_send_cmd( SCMD_PAUSE );
+    ( void )sjcd_receive_status();
+    sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+    return( 0 );
+  }
+
+  case CDROMPAUSE:{
+    struct sjcd_hw_qinfo q_info;
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: pause\n" );
+#endif
+    if( sjcd_audio_status == CDROM_AUDIO_PLAY ){
+      sjcd_send_cmd( SCMD_PAUSE );
+      ( void )sjcd_receive_status();
+      if( sjcd_get_q_info( &q_info ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+      } else {
+	sjcd_audio_status = CDROM_AUDIO_PAUSED;
+	sjcd_playing.start = q_info.abs;
+      }
+      return( 0 );
+    } else return( -EINVAL );
+  }
+
+  case CDROMRESUME:{
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: resume\n" );
+#endif
+    if( sjcd_audio_status == CDROM_AUDIO_PAUSED ){
+      /*
+       * continue play starting at saved location
+       */
+      if( sjcd_play( &sjcd_playing ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_ERROR;
+	return( -EIO );
+      } else {
+	sjcd_audio_status = CDROM_AUDIO_PLAY;
+	return( 0 );
+      }
+    } else return( -EINVAL );
+  }
+
+  case CDROMPLAYTRKIND:{
+    struct cdrom_ti ti; int s;
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: playtrkind\n" );
+#endif
+    if( ( s = verify_area( VERIFY_READ, (void *)arg, sizeof( ti ) ) ) == 0 ){
+      memcpy_fromfs( &ti, (void *)arg, sizeof( ti ) );
+
+      if( ti.cdti_trk0 < sjcd_first_track_no ) return( -EINVAL );
+      if( ti.cdti_trk1 > sjcd_last_track_no )
+	ti.cdti_trk1 = sjcd_last_track_no;
+      if( ti.cdti_trk0 > ti.cdti_trk1 ) return( -EINVAL );
+
+      sjcd_playing.start = sjcd_table_of_contents[ ti.cdti_trk0 ].un.track_msf;
+      sjcd_playing.end = ( ti.cdti_trk1 < sjcd_last_track_no ) ?
+	sjcd_table_of_contents[ ti.cdti_trk1 + 1 ].un.track_msf :
+	  sjcd_table_of_contents[ 0 ].un.track_msf;
+      
+      if( sjcd_play( &sjcd_playing ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_ERROR;
+	return( -EIO );
+      } else sjcd_audio_status = CDROM_AUDIO_PLAY;
+    }
+    return( s );
+  }
+
+  case CDROMPLAYMSF:{
+    struct cdrom_msf sjcd_msf; int s;
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: playmsf\n" );
+#endif
+    if( ( s = verify_area( VERIFY_READ, (void *)arg, sizeof( sjcd_msf ) ) ) == 0 ){
+      if( sjcd_audio_status == CDROM_AUDIO_PLAY ){
+	sjcd_send_cmd( SCMD_PAUSE );
+	( void )sjcd_receive_status();
+	sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+      }
+
+      memcpy_fromfs( &sjcd_msf, (void *)arg, sizeof( sjcd_msf ) );
+
+      sjcd_playing.start.min = bin2bcd( sjcd_msf.cdmsf_min0 );
+      sjcd_playing.start.sec = bin2bcd( sjcd_msf.cdmsf_sec0 );
+      sjcd_playing.start.frame = bin2bcd( sjcd_msf.cdmsf_frame0 );
+      sjcd_playing.end.min = bin2bcd( sjcd_msf.cdmsf_min1 );
+      sjcd_playing.end.sec = bin2bcd( sjcd_msf.cdmsf_sec1 );
+      sjcd_playing.end.frame = bin2bcd( sjcd_msf.cdmsf_frame1 );
+
+      if( sjcd_play( &sjcd_playing ) < 0 ){
+	sjcd_audio_status = CDROM_AUDIO_ERROR;
+	return( -EIO );
+      } else sjcd_audio_status = CDROM_AUDIO_PLAY;
+    }
+    return( s );
+  }
+
+  case CDROMREADTOCHDR:{
+    struct cdrom_tochdr toc_header; int s;
+#if defined (SJCD_TRACE )
+    printk( "sjcd: ioctl: readtocheader\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( toc_header ) ) ) == 0 ){
+      toc_header.cdth_trk0 = sjcd_first_track_no;
+      toc_header.cdth_trk1 = sjcd_last_track_no;
+      memcpy_tofs( (void *)arg, &toc_header, sizeof( toc_header ) );
+    }
+    return( s );
+  }
+
+  case CDROMREADTOCENTRY:{
+    struct cdrom_tocentry toc_entry; int s;
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: readtocentry\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( toc_entry ) ) ) == 0 ){
+      struct sjcd_hw_disk_info *tp;
+
+      memcpy_fromfs( &toc_entry, (void *)arg, sizeof( toc_entry ) );
+
+      if( toc_entry.cdte_track == CDROM_LEADOUT )
+	tp = &sjcd_table_of_contents[ 0 ];
+      else if( toc_entry.cdte_track < sjcd_first_track_no ) return( -EINVAL );
+      else if( toc_entry.cdte_track > sjcd_last_track_no ) return( -EINVAL );
+      else tp = &sjcd_table_of_contents[ toc_entry.cdte_track ];
+
+      toc_entry.cdte_adr = tp->track_control & 0x0F;
+      toc_entry.cdte_ctrl = tp->track_control >> 4;
+
+      switch( toc_entry.cdte_format ){
+      case CDROM_LBA:
+	toc_entry.cdte_addr.lba = msf2hsg( &( tp->un.track_msf ) );
+	break;
+      case CDROM_MSF:
+	toc_entry.cdte_addr.msf.minute = bcd2bin( tp->un.track_msf.min );
+	toc_entry.cdte_addr.msf.second = bcd2bin( tp->un.track_msf.sec );
+	toc_entry.cdte_addr.msf.frame = bcd2bin( tp->un.track_msf.frame );
+	break;
+      default: return( -EINVAL );
+      }
+      memcpy_tofs( (void *)arg, &toc_entry, sizeof( toc_entry ) );
+    }
+    return( s );
+  }
+
+  case CDROMSUBCHNL:{
+    struct cdrom_subchnl subchnl; int s;
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: subchnl\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( subchnl ) ) ) == 0 ){
+      struct sjcd_hw_qinfo q_info;
+
+      memcpy_fromfs( &subchnl, (void *)arg, sizeof( subchnl ) );
+      if( sjcd_get_q_info( &q_info ) < 0 ) return( -EIO );
+
+      subchnl.cdsc_audiostatus = sjcd_audio_status;
+      subchnl.cdsc_adr = q_info.track_control & 0x0F;
+      subchnl.cdsc_ctrl = q_info.track_control >> 4;
+      subchnl.cdsc_trk = bcd2bin( q_info.track_no );
+      subchnl.cdsc_ind = bcd2bin( q_info.x );
+
+      switch( subchnl.cdsc_format ){
+      case CDROM_LBA:
+	subchnl.cdsc_absaddr.lba = msf2hsg( &( q_info.abs ) );
+	subchnl.cdsc_reladdr.lba = msf2hsg( &( q_info.rel ) );
+	break;
+      case CDROM_MSF:
+	subchnl.cdsc_absaddr.msf.minute = bcd2bin( q_info.abs.min );
+	subchnl.cdsc_absaddr.msf.second = bcd2bin( q_info.abs.sec );
+	subchnl.cdsc_absaddr.msf.frame = bcd2bin( q_info.abs.frame );
+	subchnl.cdsc_reladdr.msf.minute = bcd2bin( q_info.rel.min );
+	subchnl.cdsc_reladdr.msf.second = bcd2bin( q_info.rel.sec );
+	subchnl.cdsc_reladdr.msf.frame = bcd2bin( q_info.rel.frame );
+	break;
+      default: return( -EINVAL );
+      }
+      memcpy_tofs( (void *)arg, &subchnl, sizeof( subchnl ) );
+    }
+    return( s );
+  }
+
+  case CDROMVOLCTRL:{
+    struct cdrom_volctrl vol_ctrl; int s;
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: volctrl\n" );
+#endif
+    if( ( s = verify_area( VERIFY_READ, (void *)arg, sizeof( vol_ctrl ) ) ) == 0 ){
+      unsigned char dummy[ 4 ];
+
+      memcpy_fromfs( &vol_ctrl, (void *)arg, sizeof( vol_ctrl ) );
+      sjcd_send_4_cmd( SCMD_SET_VOLUME, vol_ctrl.channel0, 0xFF,
+		      vol_ctrl.channel1, 0xFF );
+      if( sjcd_receive_status() < 0 ) return( -EIO );
+      ( void )sjcd_load_response( dummy, 4 );
+    }
+    return( s );
+  }
+
+  case CDROMEJECT:{
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: eject\n" );
+#endif
+    if( !sjcd_command_is_in_progress ){
+      sjcd_tray_unlock();
+      sjcd_send_cmd( SCMD_EJECT_TRAY );
+      ( void )sjcd_receive_status();
+    }
+    return( 0 );
+  }
+
+#if defined( SJCD_GATHER_STAT )
+  case 0xABCD:{
+    int s;
+#if defined( SJCD_TRACE )
+    printk( "sjcd: ioctl: statistic\n" );
+#endif
+    if( ( s = verify_area( VERIFY_WRITE, (void *)arg, sizeof( statistic ) ) ) == 0 )
+      memcpy_tofs( (void *)arg, &statistic, sizeof( statistic ) );
+    return( s );
+  }
+#endif
+
+  default:
+    return( -EINVAL );
+  }
+}
+
+/*
+ * Invalidate internal buffers of the driver.
+ */
+static void sjcd_invalidate_buffers( void ){
+  int i;
+  for( i = 0; i < SJCD_BUF_SIZ; sjcd_buf_bn[ i++ ] = -1 );
+  sjcd_buf_out = -1;
+}
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+
+#define CURRENT_IS_VALID                                      \
+    ( CURRENT != NULL && MAJOR( CURRENT->rq_dev ) == MAJOR_NR && \
+      CURRENT->cmd == READ && CURRENT->sector != -1 )
+
+static void sjcd_transfer( void ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: transfer:\n" );
+#endif
+  if( CURRENT_IS_VALID ){
+    while( CURRENT->nr_sectors ){
+      int i, bn = CURRENT->sector / 4;
+      for( i = 0; i < SJCD_BUF_SIZ && sjcd_buf_bn[ i ] != bn; i++ );
+      if( i < SJCD_BUF_SIZ ){
+	int offs = ( i * 4 + ( CURRENT->sector & 3 ) ) * 512;
+	int nr_sectors = 4 - ( CURRENT->sector & 3 );
+	if( sjcd_buf_out != i ){
+	  sjcd_buf_out = i;
+	  if( sjcd_buf_bn[ i ] != bn ){
+	    sjcd_buf_out = -1;
+	    continue;
+	  }
+	}
+	if( nr_sectors > CURRENT->nr_sectors )
+	  nr_sectors = CURRENT->nr_sectors;
+#if defined( SJCD_TRACE )
+	printk( "copy out\n" );
+#endif
+	memcpy( CURRENT->buffer, sjcd_buf + offs, nr_sectors * 512 );
+	CURRENT->nr_sectors -= nr_sectors;
+	CURRENT->sector += nr_sectors;
+	CURRENT->buffer += nr_sectors * 512;
+      } else {
+	sjcd_buf_out = -1;
+	break;
+      }
+    }
+  }
+#if defined( SJCD_TRACE )
+  printk( "sjcd: transfer: done\n" );
+#endif
+}
+
+static void sjcd_poll( void ){
+#if defined( SJCD_GATHER_STAT )
+  /*
+   * Update total number of ticks.
+   */
+  statistic.ticks++;
+  statistic.tticks[ sjcd_transfer_state ]++;
+#endif
+
+ ReSwitch: switch( sjcd_transfer_state ){
+      
+  case SJCD_S_IDLE:{
+#if defined( SJCD_GATHER_STAT )
+    statistic.idle_ticks++;
+#endif
+#if defined( SJCD_TRACE )
+    printk( "SJCD_S_IDLE\n" );
+#endif
+    return;
+  }
+
+  case SJCD_S_START:{
+#if defined( SJCD_GATHER_STAT )
+    statistic.start_ticks++;
+#endif
+    sjcd_send_cmd( SCMD_GET_STATUS );
+    sjcd_transfer_state =
+      sjcd_mode == SCMD_MODE_COOKED ? SJCD_S_READ : SJCD_S_MODE;
+    sjcd_transfer_timeout = 500;
+#if defined( SJCD_TRACE )
+    printk( "SJCD_S_START: goto SJCD_S_%s mode\n",
+	   sjcd_transfer_state == SJCD_S_READ ? "READ" : "MODE" );
+#endif
+    break;
+  }
+    
+  case SJCD_S_MODE:{
+    if( sjcd_check_status() ){
+      /*
+       * Previous command is completed.
+       */
+      if( !sjcd_status_valid || sjcd_command_failed ){
+#if defined( SJCD_TRACE )
+	printk( "SJCD_S_MODE: pre-cmd failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      sjcd_mode = 0; /* unknown mode; should not be valid when failed */
+      sjcd_send_1_cmd( SCMD_SET_MODE, SCMD_MODE_COOKED );
+      sjcd_transfer_state = SJCD_S_READ; sjcd_transfer_timeout = 1000;
+#if defined( SJCD_TRACE )
+      printk( "SJCD_S_MODE: goto SJCD_S_READ mode\n" );
+#endif
+    }
+#if defined( SJCD_GATHER_STAT )
+    else statistic.mode_ticks++;
+#endif
+    break;
+  }
+
+  case SJCD_S_READ:{
+    if( sjcd_status_valid ? 1 : sjcd_check_status() ){
+      /*
+       * Previos command is completed.
+       */
+      if( !sjcd_status_valid || sjcd_command_failed ){
+#if defined( SJCD_TRACE )
+	printk( "SJCD_S_READ: pre-cmd failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+      if( !sjcd_media_is_available ){
+#if defined( SJCD_TRACE )
+	printk( "SJCD_S_READ: no disk: goto to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+      if( sjcd_mode != SCMD_MODE_COOKED ){
+	/*
+	 * We seem to come from set mode. So discard one byte of result.
+	 */
+	if( sjcd_load_response( &sjcd_mode, 1 ) != 0 ){
+#if defined( SJCD_TRACE )
+	  printk( "SJCD_S_READ: load failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	  sjcd_transfer_state = SJCD_S_STOP;
+	  goto ReSwitch;
+	}
+	if( sjcd_mode != SCMD_MODE_COOKED ){
+#if defined( SJCD_TRACE )
+	  printk( "SJCD_S_READ: mode failed: goto to SJCD_S_STOP mode\n" );
+#endif
+	  sjcd_transfer_state = SJCD_S_STOP;
+	  goto ReSwitch;
+	}
+      }
+
+      if( CURRENT_IS_VALID ){
+	struct sjcd_play_msf msf;
+
+	sjcd_next_bn = CURRENT->sector / 4;
+	hsg2msf( sjcd_next_bn, &msf.start );
+	msf.end.min = 0; msf.end.sec = 0;            
+	msf.end.frame = sjcd_read_count = SJCD_BUF_SIZ;
+#if defined( SJCD_TRACE )
+	printk( "---reading msf-address %x:%x:%x  %x:%x:%x\n",
+	       msf.start.min, msf.start.sec, msf.start.frame,
+	       msf.end.min,   msf.end.sec,   msf.end.frame );
+	printk( "sjcd_next_bn:%x buf_in:%x buf_out:%x buf_bn:%x\n", \
+	     sjcd_next_bn, sjcd_buf_in, sjcd_buf_out,
+	     sjcd_buf_bn[ sjcd_buf_in ] );
+#endif	
+	sjcd_send_6_cmd( SCMD_DATA_READ, &msf );
+	sjcd_transfer_state = SJCD_S_DATA;
+	sjcd_transfer_timeout = 500;
+#if defined( SJCD_TRACE )
+	printk( "SJCD_S_READ: go to SJCD_S_DATA mode\n" );
+#endif
+      } else {
+#if defined( SJCD_TRACE )
+	printk( "SJCD_S_READ: nothing to read: go to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+    }
+#if defined( SJCD_GATHER_STAT )
+    else statistic.read_ticks++;
+#endif
+    break;
+  }
+
+  case SJCD_S_DATA:{
+    unsigned char stat;
+
+  sjcd_s_data: stat = inb( SJCDPORT( 1 ) );
+#if defined( SJCD_TRACE )
+    printk( "SJCD_S_DATA: status = 0x%02x\n", stat );
+#endif
+    if( SJCD_STATUS_AVAILABLE( stat ) ){
+      /*
+       * No data is waiting for us in the drive buffer. Status of operation
+       * completion is available. Read and parse it.
+       */
+      sjcd_load_status();
+
+      if( !sjcd_status_valid || sjcd_command_failed ){
+#if defined( SJCD_TRACE )
+	printk( "sjcd: read block %d failed, maybe audio disk? Giving up\n",
+	       sjcd_next_bn );
+#endif
+	if( CURRENT_IS_VALID ) end_request( 0 );
+#if defined( SJCD_TRACE )
+	printk( "SJCD_S_DATA: pre-cmd failed: go to SJCD_S_STOP mode\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      if( !sjcd_media_is_available ){
+	printk( "SJCD_S_DATA: no disk: go to SJCD_S_STOP mode\n" );
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      sjcd_transfer_state = SJCD_S_READ;
+      goto ReSwitch;
+    } else if( SJCD_DATA_AVAILABLE( stat ) ){
+      /*
+       * One frame is read into device buffer. We must copy it to our memory.
+       * Otherwise cdrom hangs up. Check to see if we have something to copy
+       * to.
+       */
+      if( !CURRENT_IS_VALID && sjcd_buf_in == sjcd_buf_out ){
+#if defined( SJCD_TRACE )
+	printk( "SJCD_S_DATA: nothing to read: go to SJCD_S_STOP mode\n" );
+	printk( " ... all the date would be discarded\n" );
+#endif
+	sjcd_transfer_state = SJCD_S_STOP;
+	goto ReSwitch;
+      }
+
+      /*
+       * Everything seems to be OK. Just read the frame and recalculate
+       * indecis.
+       */
+      sjcd_buf_bn[ sjcd_buf_in ] = -1; /* ??? */
+      insb( SJCDPORT( 2 ), sjcd_buf + 2048 * sjcd_buf_in, 2048 );
+#if defined( SJCD_TRACE )
+      printk( "SJCD_S_DATA: next_bn=%d, buf_in=%d, buf_out=%d, buf_bn=%d\n",
+	     sjcd_next_bn, sjcd_buf_in, sjcd_buf_out,
+	     sjcd_buf_bn[ sjcd_buf_in ] );
+#endif
+      sjcd_buf_bn[ sjcd_buf_in ] = sjcd_next_bn++;
+      if( sjcd_buf_out == -1 ) sjcd_buf_out = sjcd_buf_in;
+      if( ++sjcd_buf_in == SJCD_BUF_SIZ ) sjcd_buf_in = 0;
+
+      /*
+       * Only one frame is ready at time. So we should turn over to wait for
+       * another frame. If we need that, of course.
+       */
+      if( --sjcd_read_count == 0 ){
+	/*
+	 * OK, request seems to be precessed. Continue transferring...
+	 */
+	if( !sjcd_transfer_is_active ){
+	  while( CURRENT_IS_VALID ){
+	    /*
+	     * Continue transferring.
+	     */
+	    sjcd_transfer();
+	    if( CURRENT->nr_sectors == 0 ) end_request( 1 );
+	    else break;
+	  }
+	}
+	if( CURRENT_IS_VALID &&
+	   ( CURRENT->sector / 4 < sjcd_next_bn ||
+	    CURRENT->sector / 4 > sjcd_next_bn + SJCD_BUF_SIZ ) ){
+#if defined( SJCD_TRACE )
+	  printk( "SJCD_S_DATA: can't read: go to SJCD_S_STOP mode\n" );
+#endif
+	  sjcd_transfer_state = SJCD_S_STOP;
+	  goto ReSwitch;
+	}
+      }
+      /*
+       * Now we should turn around rather than wait for while.
+       */
+      goto sjcd_s_data;
+    }
+#if defined( SJCD_GATHER_STAT )
+    else statistic.data_ticks++;
+#endif
+    break;
+  }
+
+  case SJCD_S_STOP:{
+    sjcd_read_count = 0;
+    sjcd_send_cmd( SCMD_STOP );
+    sjcd_transfer_state = SJCD_S_STOPPING;
+    sjcd_transfer_timeout = 500;
+#if defined( SJCD_GATHER_STAT )
+    statistic.stop_ticks++;
+#endif
+    break;
+  }
+
+  case SJCD_S_STOPPING:{
+    unsigned char stat;
+    
+    stat = inb( SJCDPORT( 1 ) );
+#if defined( SJCD_TRACE )
+    printk( "SJCD_S_STOP: status = 0x%02x\n", stat );
+#endif      
+    if( SJCD_DATA_AVAILABLE( stat ) ){
+      int i;
+#if defined( SJCD_TRACE )
+      printk( "SJCD_S_STOP: discard data\n" );
+#endif
+      /*
+       * Discard all the data from the pipe. Foolish method.
+       */
+      for( i = 2048; i--; ( void )inb( SJCDPORT( 2 ) ) );
+      sjcd_transfer_timeout = 500;
+    } else if( SJCD_STATUS_AVAILABLE( stat ) ){
+      sjcd_load_status();
+      if( sjcd_status_valid && sjcd_media_is_changed ) {
+	sjcd_toc_uptodate = 0;
+	sjcd_invalidate_buffers();
+      }
+      if( CURRENT_IS_VALID ){
+	if( sjcd_status_valid ) sjcd_transfer_state = SJCD_S_READ;
+	else sjcd_transfer_state = SJCD_S_START;
+      } else sjcd_transfer_state = SJCD_S_IDLE;
+      goto ReSwitch;
+    }
+#if defined( SJCD_GATHER_STAT )
+    else statistic.stopping_ticks++;
+#endif
+    break;
+  }
+
+  default:
+    printk( "sjcd_poll: invalid state %d\n", sjcd_transfer_state );
+    return;
+  }
+  
+  if( --sjcd_transfer_timeout == 0 ){
+    printk( "sjcd: timeout in state %d\n", sjcd_transfer_state );
+    while( CURRENT_IS_VALID ) end_request( 0 );
+    sjcd_send_cmd( SCMD_STOP );
+    sjcd_transfer_state = SJCD_S_IDLE;
+    goto ReSwitch;
+    }
+
+  /*
+   * Get back in some time. 1 should be replaced with count variable to
+   * avoid unnecessary testings.
+   */
+  SJCD_SET_TIMER( sjcd_poll, 1 );
+}
+
+static void do_sjcd_request( void ){
+#if defined( SJCD_TRACE )
+  printk( "sjcd: do_sjcd_request(%ld+%ld)\n",
+	 CURRENT->sector, CURRENT->nr_sectors );
+#endif
+  sjcd_transfer_is_active = 1;
+  while( CURRENT_IS_VALID ){
+    /*
+     * Who of us are paranoic?
+     */
+    if( CURRENT->bh && !( CURRENT->bh->b_lock ) )
+      panic( DEVICE_NAME ": block not locked" );
+
+    sjcd_transfer();
+    if( CURRENT->nr_sectors == 0 ) end_request( 1 );
+    else {
+      sjcd_buf_out = -1;         /* Want to read a block not in buffer */
+      if( sjcd_transfer_state == SJCD_S_IDLE ){
+	if( !sjcd_toc_uptodate ){
+	  if( sjcd_update_toc() < 0 ){
+	    printk( "sjcd: transfer: discard\n" );
+	    while( CURRENT_IS_VALID ) end_request( 0 );
+	    break;
+	  }
+	}
+	sjcd_transfer_state = SJCD_S_START;
+	SJCD_SET_TIMER( sjcd_poll, HZ/100 );
+      }
+      break;
+    }
+  }
+  sjcd_transfer_is_active = 0;
+#if defined( SJCD_TRACE )
+  printk( "sjcd_next_bn:%x sjcd_buf_in:%x sjcd_buf_out:%x sjcd_buf_bn:%x\n",
+	 sjcd_next_bn, sjcd_buf_in, sjcd_buf_out, sjcd_buf_bn[ sjcd_buf_in ] );
+  printk( "do_sjcd_request ends\n" );
+#endif
+}
+
+/*
+ * Open the device special file. Check disk is in.
+ */
+int sjcd_open( struct inode *ip, struct file *fp ){
+  /*
+   * Check the presence of device.
+   */
+  if( !sjcd_present ) return( -ENXIO );
+  
+  /*
+   * Only read operations are allowed. Really? (:-)
+   */
+  if( fp->f_mode & 2 ) return( -EROFS );
+  
+  if( sjcd_open_count == 0 ){
+    int s, sjcd_open_tries;
+/* We don't know that, do we? */
+/*
+    sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
+*/
+    sjcd_mode = 0;
+    sjcd_door_was_open = 0;
+    sjcd_transfer_state = SJCD_S_IDLE;
+    sjcd_invalidate_buffers();
+    sjcd_status_valid = 0;
+
+    /*
+     * Strict status checking.
+     */
+    for( sjcd_open_tries = 4; --sjcd_open_tries; ){
+      if( !sjcd_status_valid ) sjcd_get_status();
+      if( !sjcd_status_valid ){
+#if defined( SJCD_DIAGNOSTIC )
+	printk( "sjcd: open: timed out when check status.\n" );
+#endif
+	return( -EIO );
+      } else if( !sjcd_media_is_available ){
+#if defined( SJCD_DIAGNOSTIC )
+	printk("sjcd: open: no disk in drive\n");
+#endif
+	if( !sjcd_door_closed ){
+	  sjcd_door_was_open = 1;
+#if defined( SJCD_TRACE )
+	  printk("sjcd: open: close the tray\n");
+#endif
+	  s = sjcd_tray_close();
+	  if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
+#if defined( SJCD_DIAGNOSTIC )
+	    printk("sjcd: open: tray close attempt failed\n");
+#endif
+	    return( -EIO );
+	  }
+	  continue;
+	} else return( -EIO );
+      }
+      break;
+    }
+    s = sjcd_tray_lock();
+    if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
+#if defined( SJCD_DIAGNOSTIC )
+      printk("sjcd: open: tray lock attempt failed\n");
+#endif
+      return( -EIO );
+    }
+#if defined( SJCD_TRACE )
+    printk( "sjcd: open: done\n" );
+#endif
+  }
+#ifdef MODULE
+  MOD_INC_USE_COUNT;
+#endif
+  ++sjcd_open_count;
+  return( 0 );
+}
+
+/*
+ * On close, we flush all sjcd blocks from the buffer cache.
+ */
+static void sjcd_release( struct inode *inode, struct file *file ){
+  int s;
+
+#if defined( SJCD_TRACE )
+  printk( "sjcd: release\n" );
+#endif
+#ifdef MODULE
+  MOD_DEC_USE_COUNT;
+#endif
+  if( --sjcd_open_count == 0 ){
+    sjcd_invalidate_buffers();
+    sync_dev( inode->i_rdev );
+    invalidate_buffers( inode->i_rdev );
+    s = sjcd_tray_unlock();
+    if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
+#if defined( SJCD_DIAGNOSTIC )
+      printk("sjcd: release: tray unlock attempt failed.\n");
+#endif
+    }
+    if( sjcd_door_was_open ){
+      s = sjcd_tray_open();
+      if( s < 0 || !sjcd_status_valid || sjcd_command_failed ){
+#if defined( SJCD_DIAGNOSTIC )
+	printk("sjcd: release: tray unload attempt failed.\n");
+#endif
+      }
+    }
+  }
+}
+
+/*
+ * A list of file operations allowed for this cdrom.
+ */
+static struct file_operations sjcd_fops = {
+  NULL,               /* lseek - default */
+  block_read,         /* read - general block-dev read */
+  block_write,        /* write - general block-dev write */
+  NULL,               /* readdir - bad */
+  NULL,               /* select */
+  sjcd_ioctl,         /* ioctl */
+  NULL,               /* mmap */
+  sjcd_open,          /* open */
+  sjcd_release,       /* release */
+  NULL,               /* fsync */
+  NULL,               /* fasync */
+  sjcd_disk_change,   /* media change */
+  NULL                /* revalidate */
+};
+
+/*
+ * Following stuff is intended for initialization of the cdrom. It
+ * first looks for presence of device. If the device is present, it
+ * will be reset. Then read the version of the drive and load status.
+ * The version is two BCD-coded bytes.
+ */
+static struct {
+  unsigned char major, minor;
+} sjcd_version;
+
+/*
+ * Test for presence of drive and initialize it. Called at boot time.
+ * Probe cdrom, find out version and status.
+ */
+int sjcd_init( void ){
+  int i;
+
+  if ( (isp16_type=isp16_detect()) < 0 )
+    printk( "No ISP16 cdrom interface found.\n" );
+  else {
+    u_char expected_drive;
+
+    printk( "ISP16 cdrom interface (with OPTi 82C92%s chip) detected.\n",
+      (isp16_type==2)?"9":"8" );
+
+    printk( "ISP16 sound configuration.\n" );
+    isp16_sound_config();
+
+    expected_drive = (isp16_type?ISP16_SANYO1:ISP16_SANYO0);
+
+    if ( isp16_cdi_config( sjcd_port, expected_drive, sjcd_irq, sjcd_dma ) < 0 ) {
+      printk( "ISP16 cdrom interface has not been properly configured.\n" );
+      return( -EIO );
+    }
+  }
+
+#if defined( SJCD_TRACE )
+  printk( "sjcd=0x%x,%d: ", sjcd_port, sjcd_irq );
+#endif  
+
+  if( register_blkdev( MAJOR_NR, "sjcd", &sjcd_fops ) != 0 ){
+    printk( "Unable to get major %d for Sanyo CD-ROM\n", MAJOR_NR );
+    return( -EIO );
+  }
+  
+  blk_dev[ MAJOR_NR ].request_fn = DEVICE_REQUEST;
+  read_ahead[ MAJOR_NR ] = 4;
+  
+  if( check_region( sjcd_port, 4 ) ){
+    printk( "Init failed, I/O port (%X) is already in use\n",
+      sjcd_port );
+    return( -EIO );
+  }
+  
+  /*
+   * Check for card. Since we are booting now, we can't use standard
+   * wait algorithm.
+   */
+  printk( "Sanyo: Resetting: " );
+  sjcd_send_cmd( SCMD_RESET );
+  for( i = 1000; i-- > 0 && !sjcd_status_valid; ){
+    unsigned long timer;
+
+    /*
+     * Wait 10ms approx.
+     */
+    for( timer = jiffies; jiffies <= timer; );
+    if ( (i % 100) == 0 ) printk( "." );
+    ( void )sjcd_check_status();
+  }
+  if( i == 0 || sjcd_command_failed ){
+    printk( " reset failed, no drive found.\n" );
+    return( -EIO );
+  } else printk( "\n" );
+
+  /*
+   * Get and print out cdrom version.
+   */
+  printk( "Sanyo: Getting version: " );
+  sjcd_send_cmd( SCMD_GET_VERSION );
+  for( i = 1000; i > 0 && !sjcd_status_valid; --i ){
+    unsigned long timer;
+
+    /*
+     * Wait 10ms approx.
+     */
+    for( timer = jiffies; jiffies <= timer; );
+    if ( (i % 100) == 0 ) printk( "." );
+    ( void )sjcd_check_status();
+  }
+  if( i == 0 || sjcd_command_failed ){
+    printk( " get version failed, no drive found.\n" );
+    return( -EIO );
+  }
+
+  if( sjcd_load_response( &sjcd_version, sizeof( sjcd_version ) ) == 0 ){
+    printk( " %1x.%02x\n", ( int )sjcd_version.major,
+	     ( int )sjcd_version.minor );
+  } else {
+    printk( " read version failed, no drive found.\n" );
+    return( -EIO );
+  }
+
+  /*
+   * Check and print out the tray state. (if it is needed?).
+   */
+  if( !sjcd_status_valid ){
+    printk( "Sanyo: Getting status: " );
+    sjcd_send_cmd( SCMD_GET_STATUS );
+    for( i = 1000; i > 0 && !sjcd_status_valid; --i ){
+      unsigned long timer;
+
+      /*
+       * Wait 10ms approx.
+       */
+      for( timer = jiffies; jiffies <= timer; );
+      if ( (i % 100) == 0 ) printk( "." );
+      ( void )sjcd_check_status();
+    }
+    if( i == 0 || sjcd_command_failed ){
+      printk( " get status failed, no drive found.\n" );
+      return( -EIO );
+    } else printk( "\n" );
+  }
+
+  printk( "SANYO CDR-H94A: Status: port=0x%x, irq=%d, dma=%d.\n",
+	 sjcd_port, sjcd_irq, sjcd_dma );
+
+  sjcd_present++;
+  return( 0 );
+}
+
+#ifdef MODULE
+void cleanup_module( void ){
+  if( MOD_IN_USE ){
+    printk( "sjcd: module: in use - can not remove.\n" );
+  } else if( ( unregister_blkdev( MAJOR_NR, "sjcd" ) == -EINVAL ) ){
+    printk( "sjcd: module: can not unregister device.\n" );
+  } else {
+    release_region( sjcd_port, 4 );
+    printk( "sjcd: module: removed.\n");
+  }
+}
+#endif
+
+/*
+ * -- ISP16 detection and configuration
+ *
+ *    Copyright (c) 1995, Eric van der Maarel <maarel@marin.nl>
+ *
+ *    Version 0.5
+ *
+ *    Detect cdrom interface on ISP16 soundcard.
+ *    Configure cdrom interface.
+ *    Configure sound interface.
+ *
+ *    Algorithm for the card with OPTi 82C928 taken
+ *    from the CDSETUP.SYS driver for MSDOS,
+ *    by OPTi Computers, version 2.03.
+ *    Algorithm for the card with OPTi 82C929 as communicated
+ *    to me by Vadim Model and Leo Spiekman.
+ *
+ *    Use, modifification or redistribution of this software is
+ *    allowed under the terms of the GPL.
+ *
+ */
+
+
+#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p))
+#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p))
+
+static short
+isp16_detect(void)
+{
+
+  if ( !( isp16_c929__detect() < 0 ) )
+    return(2);
+  else
+    return( isp16_c928__detect() );
+}
+
+static short
+isp16_c928__detect(void)
+{
+  u_char ctrl;
+  u_char enable_cdrom;
+  u_char io;
+  short i = -1;
+
+  isp16_ctrl = ISP16_C928__CTRL;
+  isp16_enable_port = ISP16_C928__ENABLE_PORT;
+
+  /* read' and write' are a special read and write, respectively */
+
+  /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */
+  ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC;
+  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+
+  /* read' 3,4 and 5-bit from the cdrom enable port */
+  enable_cdrom = ISP16_IN( ISP16_C928__ENABLE_PORT ) & 0x38;
+
+  if ( !(enable_cdrom & 0x20) ) {  /* 5-bit not set */
+    /* read' last 2 bits of ISP16_IO_SET_PORT */
+    io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03;
+    if ( ((io&0x01)<<1) == (io&0x02) ) {  /* bits are the same */
+      if ( io == 0 ) {  /* ...the same and 0 */
+        i = 0;
+        enable_cdrom |= 0x20;
+      }
+      else {  /* ...the same and 1 */  /* my card, first time 'round */
+        i = 1;
+        enable_cdrom |= 0x28;
+      }
+      ISP16_OUT( ISP16_C928__ENABLE_PORT, enable_cdrom );
+    }
+    else {  /* bits are not the same */
+      ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+      return(i); /* -> not detected: possibly incorrect conclusion */
+    }
+  }
+  else if ( enable_cdrom == 0x20 )
+    i = 0;
+  else if ( enable_cdrom == 0x28 )  /* my card, already initialised */
+    i = 1;
+
+  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+
+  return(i);
+}
+
+static short
+isp16_c929__detect(void)
+{
+  u_char ctrl;
+  u_char tmp;
+
+  isp16_ctrl = ISP16_C929__CTRL;
+  isp16_enable_port = ISP16_C929__ENABLE_PORT;
+
+  /* read' and write' are a special read and write, respectively */
+
+  /* read' ISP16_CTRL_PORT and save */
+  ctrl = ISP16_IN( ISP16_CTRL_PORT );
+
+  /* write' zero to the ctrl port and get response */
+  ISP16_OUT( ISP16_CTRL_PORT, 0 );
+  tmp = ISP16_IN( ISP16_CTRL_PORT );
+
+  if ( tmp != 2 )  /* isp16 with 82C929 not detected */
+    return(-1);
+
+  /* restore ctrl port value */
+  ISP16_OUT( ISP16_CTRL_PORT, ctrl );
+  
+  return(2);
+}
+
+static short
+isp16_cdi_config( int base, u_char drive_type, int irq, int dma )
+{
+  u_char base_code;
+  u_char irq_code;
+  u_char dma_code;
+  u_char i;
+
+  if ( (drive_type == ISP16_MITSUMI) && (dma != 0) )
+    printk( "Mitsumi cdrom drive has no dma support.\n" );
+
+  switch (base) {
+  case 0x340: base_code = ISP16_BASE_340; break;
+  case 0x330: base_code = ISP16_BASE_330; break;
+  case 0x360: base_code = ISP16_BASE_360; break;
+  case 0x320: base_code = ISP16_BASE_320; break;
+  default:
+    printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base );
+    return(-1);
+  }
+  switch (irq) {
+  case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */
+  case 5: irq_code = ISP16_IRQ_5;
+          printk( "Irq 5 shouldn't be used by cdrom interface on ISP16,"
+            " due to possible conflicts with the soundcard.\n");
+          break;
+  case 7: irq_code = ISP16_IRQ_7;
+          printk( "Irq 7 shouldn't be used by cdrom interface on ISP16,"
+            " due to possible conflicts with the soundcard.\n");
+          break;
+  case 3: irq_code = ISP16_IRQ_3; break;
+  case 9: irq_code = ISP16_IRQ_9; break;
+  case 10: irq_code = ISP16_IRQ_10; break;
+  case 11: irq_code = ISP16_IRQ_11; break;
+  default:
+    printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq );
+    return(-1);
+  }
+  switch (dma) {
+  case 0: dma_code = ISP16_DMA_X; break;  /* disable dma */
+  case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16,"
+            " due to conflict with the soundcard.\n");
+          return(-1); break;
+  case 3: dma_code = ISP16_DMA_3; break;
+  case 5: dma_code = ISP16_DMA_5; break;
+  case 6: dma_code = ISP16_DMA_6; break;
+  case 7: dma_code = ISP16_DMA_7; break;
+  default:
+    printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma );
+    return(-1);
+  }
+
+  if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 &&
+    drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 &&
+    drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI &&
+    drive_type != ISP16_DRIVE_X ) {
+    printk( "Drive type (code 0x%02X) not supported by cdrom"
+     " interface on ISP16.\n", drive_type );
+    return(-1);
+  }
+
+  /* set type of interface */
+  i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK;  /* clear some bits */
+  ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type );
+
+  /* enable cdrom on interface with 82C929 chip */
+  if ( isp16_type > 1 )
+    ISP16_OUT( isp16_enable_port, ISP16_ENABLE_CDROM );
+
+  /* set base address, irq and dma */
+  i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK;  /* keep some bits */
+  ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code );
+
+  return(0);
+}
+
+static void isp16_sound_config( void )
+{
+  int i;
+  u_char saved;
+
+  saved = ISP16_IN( 0xF8D ) & 0x8F;
+    
+  ISP16_OUT( 0xF8D, 0x40 );
+
+  /*
+   * Now we should wait for a while...
+   */
+  for( i = 16*1024; i--; );
+  
+  ISP16_OUT( 0xF8D, saved );
+
+  ISP16_OUT( 0xF91, 0x1B );
+
+  for( i = 5*64*1024; i != 0; i-- )
+    if( !( inb( 0x534 ) & 0x80 ) ) break;
+
+  if( i > 0 ) {
+    saved = ( inb( 0x534 ) & 0xE0 ) | 0x0A;
+    outb( saved, 0x534 );
+
+    special_mask = ( inb( 0x535 ) >> 4 ) & 0x08;
+
+    saved = ( inb( 0x534 ) & 0xE0 ) | 0x0C;
+    outb( saved, 0x534 );
+
+    switch( inb( 0x535 ) ) {
+      case 0x09:
+      case 0x0A:
+        special_mask |= 0x05;
+        break;
+      case 0x8A:
+        special_mask = 0x0F;
+        break;
+      default:
+        i = 0;
+    }
+  }
+  if ( i == 0 ) {
+    printk( "Strange MediaMagic, but\n" );
+  }
+  else {
+    printk( "Conf:" );
+    saved = inb( 0x534 ) & 0xE0;
+    for( i = 0; i < 16; i++ ) {
+      outb( 0x20 | ( u_char )i, 0x534 );
+      outb( defaults[i], 0x535 );
+    }
+    for ( i = 0; i < 16; i++ ) {
+      outb( 0x20 | ( u_char )i, 0x534 );
+      saved = inb( 0x535 );
+      printk( " %02X", saved );
+    }
+    printk( "\n" );
+  }
+
+  ISP16_OUT( 0xF91, 0xA0 | special_mask );
+
+  /*
+   * The following have no explaination yet.
+   */
+  ISP16_OUT( 0xF90, 0xA2 );
+  ISP16_OUT( 0xF92, 0x03 );
+
+  /*
+   * Turn general sound on and set total volume.
+   */
+  ISP16_OUT( 0xF93, 0x0A );
+
+/*
+  outb( 0x04, 0x224 );
+  saved = inb( 0x225 );
+  outb( 0x04, 0x224 );
+  outb( saved, 0x225 );
+*/
+
+}
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c
--- v1.3.35/linux/drivers/cdrom/sonycd535.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/cdrom/sonycd535.c	Sat Oct 21 19:43:31 1995
@@ -0,0 +1,1703 @@
+/*
+ * Sony CDU-535 interface device driver
+ *
+ * This is a modified version of the CDU-31A device driver (see below).
+ * Changes were made using documentation for the CDU-531 (which Sony
+ * assures me is very similar to the 535) and partial disassembly of the
+ * DOS driver.  I used Minyard's driver and replaced the the CDU-31A
+ * commands with the CDU-531 commands.  This was complicated by a different
+ * interface protocol with the drive.  The driver is still polled.
+ *
+ * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec.
+ * I tried polling without the sony_sleep during the data transfers but
+ * it did not speed things up any.
+ *
+ * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict
+ * with CDU-31A driver.  This is the also the number from the Linux
+ * Device Driver Registry for the Sony Drive.  Hope nobody else is using it.
+ *
+ * 1993-08-29 (rgj) remove the configuring of the interface board address
+ * from the top level configuration, you have to modify it in this file.
+ *
+ * 1995-01-26 Made module-capable (Joel Katz <Stimpson@Panix.COM>)
+ *
+ * 1995-05-20
+ *  Modified to support CDU-510/515 series
+ *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
+ *  Fixed to report verify_area() failures
+ *      (Heiko Eissfeldt <heiko@colossus.escape.de>)
+ *
+ * 1995-06-01
+ *  More changes to support CDU-510/515 series
+ *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
+ *
+ * Things to do:
+ *  - handle errors and status better, put everything into a single word
+ *  - use interrupts (code mostly there, but a big hole still missing)
+ *  - handle multi-session CDs?
+ *  - use DMA?
+ *
+ *  Known Bugs:
+ *  -
+ *
+ *   Ken Pizzini (ken@halcyon.com)
+ *
+ * Original by:
+ *   Ron Jeppesen (ronj.an@site007.saic.com)
+ *
+ *
+ *------------------------------------------------------------------------
+ * Sony CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to Ken above)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h).  With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * This interface is (unfortunately) a polled interface.  This is
+ * because most Sony interfaces are set up with DMA and interrupts
+ * disables.  Some (like mine) do not even have the capability to
+ * handle interrupts or DMA.  For this reason you will see a lot of
+ * the following:
+ *
+ *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+ *   while ((retry_count > jiffies) && (! <some condition to wait for))
+ *   {
+ *      while (handle_sony_cd_attention())
+ *         ;
+ *
+ *      sony_sleep();
+ *   }
+ *   if (the condition not met)
+ *   {
+ *      return an error;
+ *   }
+ *
+ * This ugly hack waits for something to happen, sleeping a little
+ * between every try.  it also handles attentions, which are
+ * asynchronous events from the drive informing the driver that a disk
+ * has been inserted, removed, etc.
+ *
+ * One thing about these drives: They talk in MSF (Minute Second Frame) format.
+ * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+ * disk.  The funny thing is that these are sent to the drive in BCD, but the
+ * interface wants to see them in decimal.  A lot of conversion goes on.
+ *
+ *  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.
+ *
+ */
+
+
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+	char kernel_version[]= UTS_RELEASE;
+# endif
+#define sony535_init init_module
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include <linux/cdrom.h>
+
+#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>
+
+/*
+ * this is the base address of the interface card for the Sony CDU-535
+ * CDROM drive.  If your jumpers are set for an address other than
+ * this one (the default), change the following line to the
+ * proper address.
+ */
+#ifndef CDU535_ADDRESS
+# define CDU535_ADDRESS			0x340
+#endif
+#ifndef CDU535_INTERRUPT
+# define CDU535_INTERRUPT		0
+#endif
+#ifndef CDU535_HANDLE
+# define CDU535_HANDLE			"cdu535"
+#endif
+#ifndef CDU535_MESSAGE_NAME
+# define CDU535_MESSAGE_NAME	"Sony CDU-535"
+#endif
+
+#ifndef MAX_SPINUP_RETRY
+# define MAX_SPINUP_RETRY		3	/* 1 is sufficient for most drives... */
+#endif
+#ifndef RETRY_FOR_BAD_STATUS
+# define RETRY_FOR_BAD_STATUS	100	/* in 10th of second */
+#endif
+
+#ifndef DEBUG
+# define DEBUG	1
+#endif
+
+/*
+ *  SONY535_BUFFER_SIZE determines the size of internal buffer used
+ *  by the drive.  It must be at least 2K and the larger the buffer
+ *  the better the transfer rate.  It does however take system memory.
+ *  On my system I get the following transfer rates using dd to read
+ *  10 Mb off /dev/cdrom.
+ *
+ *    8K buffer      43 Kb/sec
+ *   16K buffer      66 Kb/sec
+ *   32K buffer      91 Kb/sec
+ *   64K buffer     111 Kb/sec
+ *  128K buffer     123 Kb/sec
+ *  512K buffer     123 Kb/sec
+ */
+#define SONY535_BUFFER_SIZE	(64*1024)
+
+/*
+ *  if LOCK_DOORS is defined then the eject button is disabled while
+ * the device is open.
+ */
+#ifndef NO_LOCK_DOORS
+# define LOCK_DOORS
+#endif
+
+static int read_subcode(void);
+static void sony_get_toc(void);
+static int cdu_open(struct inode *inode, struct file *filp);
+static inline unsigned int int_to_bcd(unsigned int val);
+static unsigned int bcd_to_int(unsigned int bcd);
+static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2],
+					   Byte * response, int n_response, int ignoreStatusBit7);
+
+/* The base I/O address of the Sony Interface.  This is a variable (not a
+   #define) so it can be easily changed via some future ioctl() */
+#ifndef MODULE
+static
+#endif
+unsigned short sony535_cd_base_io = CDU535_ADDRESS;
+
+/*
+ * The following are I/O addresses of the various registers for the drive.  The
+ * comment for the base address also applies here.
+ */
+static unsigned short select_unit_reg;
+static unsigned short result_reg;
+static unsigned short command_reg;
+static unsigned short read_status_reg;
+static unsigned short data_reg;
+
+static int initialized = 0;			/* Has the drive been initialized? */
+static int sony_disc_changed = 1;	/* Has the disk been changed
+					   since the last check? */
+static int sony_toc_read = 0;		/* Has the table of contents been
+					   read? */
+static unsigned int sony_buffer_size;	/* Size in bytes of the read-ahead
+					   buffer. */
+static unsigned int sony_buffer_sectors;	/* Size (in 2048 byte records) of
+						   the read-ahead buffer. */
+static unsigned int sony_usage = 0;	/* How many processes have the
+					   drive open. */
+
+static int sony_first_block = -1;	/* First OS block (512 byte) in
+					   the read-ahead buffer */
+static int sony_last_block = -1;	/* Last OS block (512 byte) in
+					   the read-ahead buffer */
+
+static struct s535_sony_toc *sony_toc;	/* Points to the table of
+					   contents. */
+
+static struct s535_sony_subcode *last_sony_subcode;		/* Points to the last
+								   subcode address read */
+static Byte **sony_buffer;		/* Points to the pointers
+					   to the sector buffers */
+
+static int sony_inuse = 0;		/* is the drive in use? Only one
+					   open at a time allowed */
+
+/*
+ * The audio status uses the values from read subchannel data as specified
+ * in include/linux/cdrom.h.
+ */
+static int sony_audio_status = CDROM_AUDIO_NO_STATUS;
+
+/*
+ * The following are a hack for pausing and resuming audio play.  The drive
+ * does not work as I would expect it, if you stop it then start it again,
+ * the drive seeks back to the beginning and starts over.  This holds the
+ * position during a pause so a resume can restart it.  It uses the
+ * audio status variable above to tell if it is paused.
+ *   I just kept the CDU-31A driver behavior rather than using the PAUSE
+ * command on the CDU-535.
+ */
+static Byte cur_pos_msf[3] = {0, 0, 0};
+static Byte final_pos_msf[3] = {0, 0, 0};
+
+/* What IRQ is the drive using?  0 if none. */
+#ifndef MODULE
+static
+#endif
+int sony535_irq_used = CDU535_INTERRUPT;
+
+/* The interrupt handler will wake this queue up when it gets an interrupt. */
+static struct wait_queue *cdu535_irq_wait = NULL;
+
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
+ */
+static int
+cdu535_check_media_change(kdev_t full_dev)
+{
+	int retval;
+
+	if (MINOR(full_dev) != 0) {
+		printk(CDU535_MESSAGE_NAME " request error: invalid device.\n");
+		return 0;
+	}
+
+	/* if driver is not initialized, always return 0 */
+	retval = initialized ? sony_disc_changed : 0;
+	sony_disc_changed = 0;
+	return retval;
+}
+
+static inline void
+enable_interrupts(void)
+{
+#ifdef USE_IRQ
+	/* this code snarfed from cdu31a.c; it will not
+	 * directly work for the cdu535 as written...
+	 */
+	curr_control_reg |= ( SONY_ATTN_INT_EN_BIT
+						| SONY_RES_RDY_INT_EN_BIT
+						| SONY_DATA_RDY_INT_EN_BIT);
+	outb(curr_control_reg, sony_cd_control_reg);
+#endif
+}
+
+static inline void
+disable_interrupts(void)
+{
+#ifdef USE_IRQ
+	/* this code snarfed from cdu31a.c; it will not
+	 * directly work for the cdu535 as written...
+	 */
+	curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT
+						| SONY_RES_RDY_INT_EN_BIT
+						| SONY_DATA_RDY_INT_EN_BIT);
+	outb(curr_control_reg, sony_cd_control_reg);
+#endif
+}
+
+static void
+cdu535_interrupt(int irq, struct pt_regs *regs)
+{
+	disable_interrupts();
+	if (cdu535_irq_wait != NULL)
+		wake_up(&cdu535_irq_wait);
+	else
+		printk(CDU535_MESSAGE_NAME
+				": Got an interrupt but nothing was waiting\n");
+}
+
+
+/*
+ * Wait a little while (used for polling the drive).  If in initialization,
+ * setting a timeout doesn't work, so just loop for a while.  (We trust
+ * that the sony_sleep() call is protected by a test for proper jiffies count.)
+ */
+static inline void
+sony_sleep(void)
+{
+	if (sony535_irq_used <= 0) {	/* poll */
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies;
+		schedule();
+	} else {	/* Interrupt driven */
+		cli();
+		enable_interrupts();
+		interruptible_sleep_on(&cdu535_irq_wait);
+		sti();
+	}
+}
+
+/*------------------start of SONY CDU535 very specific ---------------------*/
+
+/****************************************************************************
+ * void select_unit( int unit_no )
+ *
+ *  Select the specified unit (0-3) so that subsequent commands reference it
+ ****************************************************************************/
+static void
+select_unit(int unit_no)
+{
+	unsigned int select_mask = ~(1 << unit_no);
+	outb(select_mask, select_unit_reg);
+}
+
+/***************************************************************************
+ * int read_result_reg( Byte *data_ptr )
+ *
+ *  Read a result byte from the Sony CDU controller, store in location pointed
+ * to by data_ptr.  Return zero on success, TIME_OUT if we did not receive
+ * data.
+ ***************************************************************************/
+static int
+read_result_reg(Byte *data_ptr)
+{
+	int retry_count;
+	int read_status;
+
+	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+	while (jiffies < retry_count) {
+		if (((read_status = inb(read_status_reg)) & SONY535_RESULT_NOT_READY_BIT) == 0) {
+#if DEBUG > 1
+			printk(CDU535_MESSAGE_NAME
+					": read_result_reg(): readStatReg = 0x%x\n", read_status);
+#endif
+			*data_ptr = inb(result_reg);
+			return 0;
+		} else {
+			sony_sleep();
+		}
+	}
+	printk(CDU535_MESSAGE_NAME " read_result_reg: TIME OUT!\n");
+	return TIME_OUT;
+}
+
+/****************************************************************************
+ * int read_exec_status( Byte status[2] )
+ *
+ *  Read the execution status of the last command and put into status.
+ * Handles reading second status word if available.  Returns 0 on success,
+ * TIME_OUT on failure.
+ ****************************************************************************/
+static int
+read_exec_status(Byte status[2])
+{
+	status[1] = 0;
+	if (read_result_reg(&(status[0])) != 0)
+		return TIME_OUT;
+	if ((status[0] & 0x80) != 0) {	/* byte two follows */
+		if (read_result_reg(&(status[1])) != 0)
+			return TIME_OUT;
+	}
+#if DEBUG > 1
+	printk(CDU535_MESSAGE_NAME ": read_exec_status: read 0x%x 0x%x\n",
+			status[0], status[1]);
+#endif
+	return 0;
+}
+
+/****************************************************************************
+ * int check_drive_status( void )
+ *
+ *  Check the current drive status.  Using this before executing a command
+ * takes care of the problem of unsolicited drive status-2 messages.
+ * Add a check of the audio status if we think the disk is playing.
+ ****************************************************************************/
+static int
+check_drive_status(void)
+{
+	Byte status, e_status[2];
+	int  CDD, ATN;
+	Byte cmd;
+
+	select_unit(0);
+	if (sony_audio_status == CDROM_AUDIO_PLAY) {	/* check status */
+		outb(SONY535_REQUEST_AUDIO_STATUS, command_reg);
+		if (read_result_reg(&status) == 0) {
+			switch (status) {
+			case 0x0:
+				break;		/* play in progress */
+			case 0x1:
+				break;		/* paused */
+			case 0x3:		/* audio play completed */
+			case 0x5:		/* play not requested */
+				sony_audio_status = CDROM_AUDIO_COMPLETED;
+				read_subcode();
+				break;
+			case 0x4:		/* error during play */
+				sony_audio_status = CDROM_AUDIO_ERROR;
+				break;
+			}
+		}
+	}
+	/* now check drive status */
+	outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg);
+	if (read_result_reg(&status) != 0)
+		return TIME_OUT;
+
+#if DEBUG > 1
+	printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status);
+#endif
+
+	if (status == 0)
+		return 0;
+
+	ATN = status & 0xf;
+	CDD = (status >> 4) & 0xf;
+
+	switch (ATN) {
+	case 0x0:
+		break;					/* go on to CDD stuff */
+	case SONY535_ATN_BUSY:
+		if (initialized)
+			printk(CDU535_MESSAGE_NAME " error: drive busy\n");
+		return CD_BUSY;
+	case SONY535_ATN_EJECT_IN_PROGRESS:
+		printk(CDU535_MESSAGE_NAME " error: eject in progress\n");
+		sony_audio_status = CDROM_AUDIO_INVALID;
+		return CD_BUSY;
+	case SONY535_ATN_RESET_OCCURRED:
+	case SONY535_ATN_DISC_CHANGED:
+	case SONY535_ATN_RESET_AND_DISC_CHANGED:
+#if DEBUG > 0
+		printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n");
+#endif
+		sony_disc_changed = 1;
+		sony_toc_read = 0;
+		sony_audio_status = CDROM_AUDIO_NO_STATUS;
+		sony_first_block = -1;
+		sony_last_block = -1;
+		if (initialized) {
+			cmd = SONY535_SPIN_UP;
+			do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0);
+			sony_get_toc();
+		}
+		return 0;
+	default:
+		printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN);
+		return CD_BUSY;
+	}
+	switch (CDD) {			/* the 531 docs are not helpful in decoding this */
+	case 0x0:				/* just use the values from the DOS driver */
+	case 0x2:
+	case 0xa:
+		break;				/* no error */
+	case 0xc:
+		printk(CDU535_MESSAGE_NAME
+				": check_drive_status(): CDD = 0xc! Not properly handled!\n");
+		return CD_BUSY;		/* ? */
+	default:
+		return CD_BUSY;
+	}
+	return 0;
+}	/* check_drive_status() */
+
+/*****************************************************************************
+ * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2],
+ *                Byte *response, int n_response, int ignore_status_bit7 )
+ *
+ *  Generic routine for executing commands.  The command and its parameters
+ *  should be placed in the cmd[] array, number of bytes in the command is
+ *  stored in nCmd.  The response from the command will be stored in the
+ *  response array.  The number of bytes you expect back (excluding status)
+ *  should be passed in n_response.  Finally, some
+ *  commands set bit 7 of the return status even when there is no second
+ *  status byte, on these commands set ignoreStatusBit7 TRUE.
+ *    If the command was sent and data received back, then we return 0,
+ *  else we return TIME_OUT.  You still have to check the status yourself.
+ *    You should call check_drive_status() before calling this routine
+ *  so that you do not lose notifications of disk changes, etc.
+ ****************************************************************************/
+static int
+do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2],
+			Byte * response, int n_response, int ignore_status_bit7)
+{
+	int i;
+
+	/* write out the command */
+	for (i = 0; i < n_cmd; i++)
+		outb(cmd[i], command_reg);
+
+	/* read back the status */
+	if (read_result_reg(status) != 0)
+		return TIME_OUT;
+	if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) {
+		/* get second status byte */
+		if (read_result_reg(status + 1) != 0)
+			return TIME_OUT;
+	} else {
+		status[1] = 0;
+	}
+#if DEBUG > 2
+	printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n",
+			*cmd, status[0], status[1]);
+#endif
+
+	/* do not know about when I should read set of data and when not to */
+	if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0)
+		return 0;
+
+	/* else, read in rest of data */
+	for (i = 0; 0 < n_response; n_response--, i++)
+		if (read_result_reg(response + i) != 0)
+			return TIME_OUT;
+	return 0;
+}	/* do_sony_cmd() */
+
+/**************************************************************************
+ * int set_drive_mode( int mode, Byte status[2] )
+ *
+ *  Set the drive mode to the specified value (mode=0 is audio, mode=e0
+ * is mode-1 CDROM
+ **************************************************************************/
+static int
+set_drive_mode(int mode, Byte status[2])
+{
+	Byte cmd_buff[2];
+	Byte ret_buff[1];
+
+	cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+	cmd_buff[1] = mode;
+	return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1);
+}
+
+/***************************************************************************
+ * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2],
+ *                             Byte *data_buff, int buff_size )
+ *
+ *  Read n_blocks of data from the CDROM starting at position params[0:2],
+ *  number of blocks in stored in params[3:5] -- both these are already
+ *  int bcd format.
+ *  Transfer the data into the buffer pointed at by data_buff.  buff_size
+ *  gives the number of bytes available in the buffer.
+ *    The routine returns number of bytes read in if successful, otherwise
+ *  it returns one of the standard error returns.
+ ***************************************************************************/
+static int
+seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
+					   Byte **buff, int buf_size)
+{
+	const int block_size = 2048;
+	Byte cmd_buff[7];
+	int  i;
+	int  read_status;
+	int  retry_count;
+	Byte *data_buff;
+	int  sector_count = 0;
+
+	if (buf_size < ((long)block_size) * n_blocks)
+		return NO_ROOM;
+
+	set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
+
+	/* send command to read the data */
+	cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
+	for (i = 0; i < 6; i++)
+		cmd_buff[i + 1] = params[i];
+	for (i = 0; i < 7; i++)
+		outb(cmd_buff[i], command_reg);
+
+	/* read back the data one block at a time */
+	while (0 < n_blocks--) {
+		/* wait for data to be ready */
+		retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+		while (jiffies < retry_count) {
+			read_status = inb(read_status_reg);
+			if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
+				read_exec_status(status);
+				return BAD_STATUS;
+			}
+			if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
+				/* data is ready, read it */
+				data_buff = buff[sector_count++];
+				for (i = 0; i < block_size; i++)
+					*data_buff++ = inb(data_reg);	/* unrolling this loop does not seem to help */
+				break;			/* exit the timeout loop */
+			}
+			sony_sleep();		/* data not ready, sleep a while */
+		}
+		if (retry_count <= jiffies)
+			return TIME_OUT;	/* if we reach this stage */
+	}
+
+	/* read all the data, now read the status */
+	if ((i = read_exec_status(status)) != 0)
+		return i;
+	return block_size * sector_count;
+}	/* seek_and_read_N_blocks() */
+
+/****************************************************************************
+ * int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
+ *
+ *  Read in the table of contents data.  Converts all the bcd data
+ * into integers in the toc structure.
+ ****************************************************************************/
+static int
+request_toc_data(Byte status[2], struct s535_sony_toc *toc)
+{
+	int  to_status;
+	int  i, j, n_tracks, track_no;
+	int  first_track_num, last_track_num;
+	Byte cmd_no = 0xb2;
+	Byte track_address_buffer[5];
+
+	/* read the fixed portion of the table of contents */
+	if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0)
+		return to_status;
+
+	/* convert the data into integers so we can use them */
+	first_track_num = bcd_to_int(toc->first_track_num);
+	last_track_num = bcd_to_int(toc->last_track_num);
+	n_tracks = last_track_num - first_track_num + 1;
+
+	/* read each of the track address descriptors */
+	for (i = 0; i < n_tracks; i++) {
+		/* read the descriptor into a temporary buffer */
+		for (j = 0; j < 5; j++) {
+			if (read_result_reg(track_address_buffer + j) != 0)
+				return TIME_OUT;
+			if (j == 1)		/* need to convert from bcd */
+				track_no = bcd_to_int(track_address_buffer[j]);
+		}
+		/* copy the descriptor to proper location - sonycd.c just fills */
+		memcpy(toc->tracks + i, track_address_buffer, 5);
+	}
+	return 0;
+}	/* request_toc_data() */
+
+/***************************************************************************
+ * int spin_up_drive( Byte status[2] )
+ *
+ *  Spin up the drive (unless it is already spinning).
+ ***************************************************************************/
+static int
+spin_up_drive(Byte status[2])
+{
+	Byte cmd;
+
+	/* first see if the drive is already spinning */
+	cmd = SONY535_REQUEST_DRIVE_STATUS_1;
+	if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0)
+		return TIME_OUT;
+	if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0)
+		return 0;	/* it's already spinning */
+
+	/* otherwise, give the spin-up command */
+	cmd = SONY535_SPIN_UP;
+	return do_sony_cmd(&cmd, 1, status, NULL, 0, 0);
+}
+
+/*--------------------end of SONY CDU535 very specific ---------------------*/
+
+/* Convert from an integer 0-99 to BCD */
+static inline unsigned int
+int_to_bcd(unsigned int val)
+{
+	int retval;
+
+	retval = (val / 10) << 4;
+	retval = retval | val % 10;
+	return retval;
+}
+
+
+/* Convert from BCD to an integer from 0-99 */
+static unsigned int
+bcd_to_int(unsigned int bcd)
+{
+	return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f);
+}
+
+
+/*
+ * Convert a logical sector value (like the OS would want to use for
+ * a block device) to an MSF format.
+ */
+static void
+log_to_msf(unsigned int log, Byte *msf)
+{
+	log = log + LOG_START_OFFSET;
+	msf[0] = int_to_bcd(log / 4500);
+	log = log % 4500;
+	msf[1] = int_to_bcd(log / 75);
+	msf[2] = int_to_bcd(log % 75);
+}
+
+
+/*
+ * Convert an MSF format to a logical sector.
+ */
+static unsigned int
+msf_to_log(Byte *msf)
+{
+	unsigned int log;
+
+
+	log = bcd_to_int(msf[2]);
+	log += bcd_to_int(msf[1]) * 75;
+	log += bcd_to_int(msf[0]) * 4500;
+	log = log - LOG_START_OFFSET;
+
+	return log;
+}
+
+
+/*
+ * Take in integer size value and put it into a buffer like
+ * the drive would want to see a number-of-sector value.
+ */
+static void
+size_to_buf(unsigned int size, Byte *buf)
+{
+	buf[0] = size / 65536;
+	size = size % 65536;
+	buf[1] = size / 256;
+	buf[2] = size % 256;
+}
+
+
+/*
+ * 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
+ * bytes to help speed operations.  This especially helps since the OS
+ * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
+ * data access on a CD is done sequentially, this saves a lot of operations.
+ */
+static void
+do_cdu535_request(void)
+{
+	unsigned int dev;
+	unsigned int read_size;
+	int  block;
+	int  nsect;
+	int  copyoff;
+	int  spin_up_retry;
+	Byte params[10];
+	Byte status[2];
+	Byte cmd[2];
+
+	if (!sony_inuse) {
+		cdu_open(NULL, NULL);
+	}
+	while (1) {
+		/*
+		 * The beginning here is stolen from the hard disk driver.  I hope
+		 * it's right.
+		 */
+		if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) {
+			return;
+		}
+		INIT_REQUEST;
+		dev = MINOR(CURRENT->rq_dev);
+		block = CURRENT->sector;
+		nsect = CURRENT->nr_sectors;
+		if (dev != 0) {
+			end_request(0);
+			continue;
+		}
+		switch (CURRENT->cmd) {
+		case READ:
+			/*
+			 * If the block address is invalid or the request goes beyond the end of
+			 * the media, return an error.
+			 */
+
+			if (sony_toc->lead_out_start_lba <= (block / 4)) {
+				end_request(0);
+				return;
+			}
+			if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) {
+				end_request(0);
+				return;
+			}
+			while (0 < nsect) {
+				/*
+				 * If the requested sector is not currently in the read-ahead buffer,
+				 * it must be read in.
+				 */
+				if ((block < sony_first_block) || (sony_last_block < block)) {
+					sony_first_block = (block / 4) * 4;
+					log_to_msf(block / 4, params);
+
+					/*
+					 * If the full read-ahead would go beyond the end of the media, trim
+					 * it back to read just till the end of the media.
+					 */
+					if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) {
+						sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
+						read_size = sony_toc->lead_out_start_lba - (block / 4);
+					} else {
+						sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
+						read_size = sony_buffer_sectors;
+					}
+					size_to_buf(read_size, &params[3]);
+
+					/*
+					 * Read the data.  If the drive was not spinning,
+					 * spin it up and try some more.
+					 */
+					for (spin_up_retry=0 ;; ++spin_up_retry) {
+						/* This loop has been modified to support the Sony
+						 * CDU-510/515 series, thanks to Claudio Porfiri 
+						 * <C.Porfiri@nisms.tei.ericsson.se>.
+						 */
+						/*
+						 * This part is to deal with very slow hardware.  We
+						 * try at most MAX_SPINUP_RETRY times to read the same
+						 * block.  A check for seek_and_read_N_blocks' result is
+						 * performed; if the result is wrong, the CDROM's engine
+						 * is restarted and the operation is tried again.
+						 */
+						/*
+						 * 1995-06-01: The system got problems when downloading
+						 * from Slackware CDROM, the problem seems to be:
+						 * seek_and_read_N_blocks returns BAD_STATUS and we
+						 * should wait for a while before retrying, so a new
+						 * part was added to discriminate the return value from
+						 * seek_and_read_N_blocks for the various cases.
+						 */
+						int readStatus = seek_and_read_N_blocks(params, read_size,
+									status, sony_buffer, (read_size * 2048));
+						if (0 <= readStatus)	/* Good data; common case, placed first */
+							break;
+						if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) {
+							/* give up */
+							if (readStatus == NO_ROOM)
+								printk(CDU535_MESSAGE_NAME " No room to read from CD\n");
+							else
+								printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n",
+										status[0]);
+							sony_first_block = -1;
+							sony_last_block = -1;
+							end_request(0);
+							return;
+						}
+						if (readStatus == BAD_STATUS) {
+							/* Sleep for a while, then retry */
+							current->state = TASK_INTERRUPTIBLE;
+							current->timeout = jiffies + RETRY_FOR_BAD_STATUS;
+							schedule();
+						}
+#if DEBUG > 0
+						printk(CDU535_MESSAGE_NAME
+							" debug: calling spin up when reading data!\n");
+#endif
+						cmd[0] = SONY535_SPIN_UP;
+						do_sony_cmd(cmd, 1, status, NULL, 0, 0);
+					}
+				}
+				/*
+				 * The data is in memory now, copy it to the buffer and advance to the
+				 * next block to read.
+				 */
+				copyoff = block - sony_first_block;
+				memcpy(CURRENT->buffer,
+					   sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512);
+
+				block += 1;
+				nsect -= 1;
+				CURRENT->buffer += 512;
+			}
+
+			end_request(1);
+			break;
+
+		case WRITE:
+			end_request(0);
+			break;
+
+		default:
+			panic("Unknown SONY CD cmd");
+		}
+	}
+}
+
+
+/*
+ * Read the table of contents from the drive and set sony_toc_read if
+ * successful.
+ */
+static void
+sony_get_toc(void)
+{
+	Byte status[2];
+	if (!sony_toc_read) {
+		/* do not call check_drive_status() from here since it can call this routine */
+		if (request_toc_data(status, sony_toc) < 0)
+			return;
+		sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+		sony_toc_read = 1;
+	}
+}
+
+
+/*
+ * Search for a specific track in the table of contents.  track is
+ * passed in bcd format
+ */
+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++) {
+		if (sony_toc->tracks[i].track == track) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+/*
+ * Read the subcode and put it int last_sony_subcode for future use.
+ */
+static int
+read_subcode(void)
+{
+	Byte cmd = SONY535_REQUEST_SUB_Q_DATA;
+	Byte status[2];
+	int  dsc_status;
+
+	if (check_drive_status() != 0)
+		return -EIO;
+
+	if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode,
+							   sizeof(struct s535_sony_subcode), 1)) != 0) {
+		printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n",
+				status[0], dsc_status);
+		return -EIO;
+	}
+	return 0;
+}
+
+
+/*
+ * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
+ * the drive is playing, the subchannel needs to be read (since it would be
+ * changing).  If the drive is paused or completed, the subcode information has
+ * already been stored, just use that.  The ioctl call wants things in decimal
+ * (not BCD), so all the conversions are done.
+ */
+static int
+sony_get_subchnl_info(long arg)
+{
+	struct cdrom_subchnl schi;
+	int err;
+
+	/* Get attention stuff */
+	if (check_drive_status() != 0)
+		return -EIO;
+
+	sony_get_toc();
+	if (!sony_toc_read) {
+		return -EIO;
+	}
+	err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi);
+	if (err)
+		return err;
+
+	memcpy_fromfs(&schi, (char *)arg, sizeof schi);
+
+	switch (sony_audio_status) {
+	case CDROM_AUDIO_PLAY:
+		if (read_subcode() < 0) {
+			return -EIO;
+		}
+		break;
+
+	case CDROM_AUDIO_PAUSED:
+	case CDROM_AUDIO_COMPLETED:
+		break;
+
+	case CDROM_AUDIO_NO_STATUS:
+		schi.cdsc_audiostatus = sony_audio_status;
+		memcpy_tofs((char *)arg, &schi, sizeof schi);
+		return 0;
+		break;
+
+	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]);
+	} 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);
+	}
+	memcpy_tofs((char *)arg, &schi, sizeof schi);
+	return 0;
+}
+
+
+/*
+ * The big ugly ioctl handler.
+ */
+static int
+cdu_ioctl(struct inode *inode,
+		  struct file *file,
+		  unsigned int cmd,
+		  unsigned long arg)
+{
+	unsigned int dev;
+	Byte status[2];
+	Byte cmd_buff[10], params[10];
+	int  i;
+	int  dsc_status;
+	int  err;
+
+	if (!inode) {
+		return -EINVAL;
+	}
+	dev = MINOR(inode->i_rdev) >> 6;
+	if (dev != 0) {
+		return -EINVAL;
+	}
+	if (check_drive_status() != 0)
+		return -EIO;
+
+	switch (cmd) {
+	case CDROMSTART:			/* Spin up the drive */
+		if (spin_up_drive(status) < 0) {
+			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n",
+					status[0]);
+			return -EIO;
+		}
+		return 0;
+		break;
+
+	case CDROMSTOP:			/* Spin down the drive */
+		cmd_buff[0] = SONY535_HOLD;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+
+		/*
+		 * Spin the drive down, ignoring the error if the disk was
+		 * already not spinning.
+		 */
+		sony_audio_status = CDROM_AUDIO_NO_STATUS;
+		cmd_buff[0] = SONY535_SPIN_DOWN;
+		dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+		if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) ||
+			((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) {
+			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n",
+					status[0]);
+			return -EIO;
+		}
+		return 0;
+		break;
+
+	case CDROMPAUSE:			/* Pause the drive */
+		cmd_buff[0] = SONY535_HOLD;		/* CDU-31 driver uses AUDIO_STOP, not pause */
+		if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
+			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n",
+					status[0]);
+			return -EIO;
+		}
+		/* Get the current position and save it for resuming */
+		if (read_subcode() < 0) {
+			return -EIO;
+		}
+		cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
+		cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
+		cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
+		sony_audio_status = CDROM_AUDIO_PAUSED;
+		return 0;
+		break;
+
+	case CDROMRESUME:			/* Start the drive after being paused */
+		set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+
+		if (sony_audio_status != CDROM_AUDIO_PAUSED) {
+			return -EINVAL;
+		}
+		spin_up_drive(status);
+
+		/* Start the drive at the saved position. */
+		cmd_buff[0] = SONY535_PLAY_AUDIO;
+		cmd_buff[1] = 0;		/* play back starting at this address */
+		cmd_buff[2] = cur_pos_msf[0];
+		cmd_buff[3] = cur_pos_msf[1];
+		cmd_buff[4] = cur_pos_msf[2];
+		cmd_buff[5] = SONY535_PLAY_AUDIO;
+		cmd_buff[6] = 2;		/* set ending address */
+		cmd_buff[7] = final_pos_msf[0];
+		cmd_buff[8] = final_pos_msf[1];
+		cmd_buff[9] = final_pos_msf[2];
+		if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+			(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n",
+					status[0]);
+			return -EIO;
+		}
+		sony_audio_status = CDROM_AUDIO_PLAY;
+		return 0;
+		break;
+
+	case CDROMPLAYMSF:			/* Play starting at the given MSF address. */
+		err = verify_area(VERIFY_READ, (char *)arg, 6);
+		if (err)
+			return err;
+		spin_up_drive(status);
+		set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+		memcpy_fromfs(params, (void *)arg, 6);
+
+		/* The parameters are given in int, must be converted */
+		for (i = 0; i < 3; i++) {
+			cmd_buff[2 + i] = int_to_bcd(params[i]);
+			cmd_buff[7 + i] = int_to_bcd(params[i + 3]);
+		}
+		cmd_buff[0] = SONY535_PLAY_AUDIO;
+		cmd_buff[1] = 0;		/* play back starting at this address */
+		/* cmd_buff[2-4] are filled in for loop above */
+		cmd_buff[5] = SONY535_PLAY_AUDIO;
+		cmd_buff[6] = 2;		/* set ending address */
+		/* cmd_buff[7-9] are filled in for loop above */
+		if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+			(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n",
+					status[0]);
+			return -EIO;
+		}
+		/* Save the final position for pauses and resumes */
+		final_pos_msf[0] = cmd_buff[7];
+		final_pos_msf[1] = cmd_buff[8];
+		final_pos_msf[2] = cmd_buff[9];
+		sony_audio_status = CDROM_AUDIO_PLAY;
+		return 0;
+		break;
+
+	case CDROMREADTOCHDR:		/* Read the table of contents header */
+		{
+			struct cdrom_tochdr *hdr;
+			struct cdrom_tochdr loc_hdr;
+
+			sony_get_toc();
+			if (!sony_toc_read)
+				return -EIO;
+			hdr = (struct cdrom_tochdr *)arg;
+			err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr);
+			if (err)
+				return err;
+			loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+			loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
+			memcpy_tofs(hdr, &loc_hdr, sizeof *hdr);
+		}
+		return 0;
+		break;
+
+	case CDROMREADTOCENTRY:	/* Read a given table of contents entry */
+		{
+			struct cdrom_tocentry *entry;
+			struct cdrom_tocentry loc_entry;
+			int  track_idx;
+			Byte *msf_val = NULL;
+
+			sony_get_toc();
+			if (!sony_toc_read) {
+				return -EIO;
+			}
+			entry = (struct cdrom_tocentry *)arg;
+			err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry);
+			if (err)
+				return err;
+
+			memcpy_fromfs(&loc_entry, entry, sizeof loc_entry);
+
+			/* Lead out is handled separately since it is special. */
+			if (loc_entry.cdte_track == CDROM_LEADOUT) {
+				loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ;
+				loc_entry.cdte_ctrl = sony_toc->control2;
+				msf_val = sony_toc->lead_out_start_msf;
+			} else {
+				track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
+				if (track_idx < 0)
+					return -EINVAL;
+				loc_entry.cdte_adr = 0 /*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;
+			}
+
+			/* Logical buffer address or MSF format requested? */
+			if (loc_entry.cdte_format == CDROM_LBA) {
+				loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+			} else if (loc_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));
+			}
+			memcpy_tofs(entry, &loc_entry, sizeof *entry);
+		}
+		return 0;
+		break;
+
+	case CDROMPLAYTRKIND:		/* Play a track.  This currently ignores index. */
+		{
+			struct cdrom_ti ti;
+			int track_idx;
+
+			sony_get_toc();
+			if (!sony_toc_read)
+				return -EIO;
+			err = verify_area(VERIFY_READ, (char *)arg, sizeof ti);
+			if (err)
+				return err;
+
+			memcpy_fromfs(&ti, (char *)arg, sizeof ti);
+			if ((ti.cdti_trk0 < sony_toc->first_track_num)
+				|| (sony_toc->last_track_num < ti.cdti_trk0)
+				|| (ti.cdti_trk1 < ti.cdti_trk0)) {
+				return -EINVAL;
+			}
+			track_idx = find_track(int_to_bcd(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];
+			/*
+			 * If we want to stop after the last track, use the lead-out
+			 * MSF to do that.
+			 */
+			if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) {
+				log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1,
+						   &(params[4]));
+			} 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]));
+			}
+			params[0] = 0x03;
+
+			spin_up_drive(status);
+
+			set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+
+			/* Start the drive at the saved position. */
+			cmd_buff[0] = SONY535_PLAY_AUDIO;
+			cmd_buff[1] = 0;	/* play back starting at this address */
+			cmd_buff[2] = params[1];
+			cmd_buff[3] = params[2];
+			cmd_buff[4] = params[3];
+			cmd_buff[5] = SONY535_PLAY_AUDIO;
+			cmd_buff[6] = 2;	/* set ending address */
+			cmd_buff[7] = params[4];
+			cmd_buff[8] = params[5];
+			cmd_buff[9] = params[6];
+			if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+				(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+				printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n",
+						status[0]);
+				printk("... Params: %x %x %x %x %x %x %x\n",
+						params[0], params[1], params[2],
+						params[3], params[4], params[5], params[6]);
+				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];
+			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;
+
+			err = verify_area(VERIFY_READ, (char *)arg, sizeof volctrl);
+			if (err)
+				return err;
+
+			memcpy_fromfs(&volctrl, (char *)arg, sizeof volctrl);
+			cmd_buff[0] = SONY535_SET_VOLUME;
+			cmd_buff[1] = volctrl.channel0;
+			cmd_buff[2] = volctrl.channel1;
+			if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) {
+				printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n",
+						status[0]);
+				return -EIO;
+			}
+		}
+		return 0;
+
+	case CDROMEJECT:			/* Eject the drive */
+		cmd_buff[0] = SONY535_STOP;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+		cmd_buff[0] = SONY535_SPIN_DOWN;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+
+		sony_audio_status = CDROM_AUDIO_INVALID;
+		cmd_buff[0] = SONY535_EJECT_CADDY;
+		if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
+			printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n",
+					status[0]);
+			return -EIO;
+		}
+		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
+cdu_open(struct inode *inode,
+		 struct file *filp)
+{
+	Byte status[2], cmd_buff[2];
+
+
+	if (sony_inuse)
+		return -EBUSY;
+	if (check_drive_status() != 0)
+		return -EIO;
+	sony_inuse = 1;
+	MOD_INC_USE_COUNT;
+
+	if (spin_up_drive(status) != 0) {
+		printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n",
+				status[0]);
+		sony_inuse = 0;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	sony_get_toc();
+	if (!sony_toc_read) {
+		cmd_buff[0] = SONY535_SPIN_DOWN;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+		sony_inuse = 0;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	if (inode) {
+		check_disk_change(inode->i_rdev);
+	}
+	sony_usage++;
+
+#ifdef LOCK_DOORS
+	/* disable the eject button while mounted */
+	cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON;
+	do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+#endif
+
+	return 0;
+}
+
+
+/*
+ * 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 void
+cdu_release(struct inode *inode,
+			struct file *filp)
+{
+	Byte status[2], cmd_no;
+
+	sony_inuse = 0;
+	MOD_DEC_USE_COUNT;
+
+	if (0 < sony_usage) {
+		sony_usage--;
+	}
+	if (sony_usage == 0) {
+		sync_dev(inode->i_rdev);
+		check_drive_status();
+
+		if (sony_audio_status != CDROM_AUDIO_PLAY) {
+			cmd_no = SONY535_SPIN_DOWN;
+			do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
+		}
+#ifdef LOCK_DOORS
+		/* enable the eject button after umount */
+		cmd_no = SONY535_ENABLE_EJECT_BUTTON;
+		do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
+#endif
+	}
+}
+
+
+static struct file_operations cdu_fops =
+{
+	NULL,						/* lseek - default */
+	block_read,					/* read - general block-dev read */
+	block_write,				/* write - general block-dev write */
+	NULL,						/* readdir - bad */
+	NULL,						/* select */
+	cdu_ioctl,					/* ioctl */
+	NULL,						/* mmap */
+	cdu_open,					/* open */
+	cdu_release,				/* release */
+	NULL,						/* fsync */
+	NULL,						/* fasync */
+	cdu535_check_media_change,	/* check media change */
+	NULL						/* revalidate */
+};
+
+/*
+ * Initialize the driver.
+ */
+int
+sony535_init(void)
+{
+	struct s535_sony_drive_config drive_config;
+	Byte cmd_buff[3];
+	Byte ret_buff[2];
+	Byte status[2];
+	int  retry_count;
+	int  tmp_irq;
+	int  i;
+
+	/* Setting the base I/O address to 0 will disable it. */
+	if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0))
+		return 0;
+
+	/* Set up all the register locations */
+	result_reg = sony535_cd_base_io;
+	command_reg = sony535_cd_base_io;
+	data_reg = sony535_cd_base_io + 1;
+	read_status_reg = sony535_cd_base_io + 2;
+	select_unit_reg = sony535_cd_base_io + 3;
+
+#ifndef USE_IRQ
+	sony535_irq_used = 0;	/* polling only until this is ready... */
+#endif
+	/* we need to poll until things get initialized */
+	tmp_irq = sony535_irq_used;
+	sony535_irq_used = 0;
+
+#if DEBUG > 0
+	printk(CDU535_MESSAGE_NAME ": probing base address %03X\n",
+			sony535_cd_base_io);
+#endif
+	if (check_region(sony535_cd_base_io,4)) {
+		printk(CDU535_MESSAGE_NAME ": my base address is not free!\n");
+		return -EIO;
+	}
+	/* look for the CD-ROM, follows the procedure in the DOS driver */
+	inb(select_unit_reg);
+	retry_count = jiffies + 2 * HZ;
+	while (jiffies < retry_count)
+		sony_sleep();			/* wait for 40 18 Hz ticks (from DOS driver) */
+	inb(result_reg);
+
+	outb(0, read_status_reg);	/* does a reset? */
+	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+	while (jiffies < retry_count) {
+		select_unit(0);
+		if (inb(result_reg) != 0xff)
+			break;
+		sony_sleep();
+	}
+
+	if ((jiffies < retry_count) && (check_drive_status() != TIME_OUT)) {
+		/* CD-ROM drive responded --  get the drive configuration */
+		cmd_buff[0] = SONY535_INQUIRY;
+		if (do_sony_cmd(cmd_buff, 1, status,
+						(Byte *)&drive_config, 28, 1) == 0) {
+			/* was able to get the configuration,
+			 * set drive mode as rest of init
+			 */
+#if DEBUG > 0
+			/* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */
+			if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 )
+				printk(CDU535_MESSAGE_NAME
+						"Inquiry command returned status = 0x%x\n", status[0]);
+#endif
+			/* now ready to use interrupts, if available */
+			sony535_irq_used = tmp_irq;
+#ifndef MODULE
+/* This code is not in MODULEs by default, since the autoirq stuff might
+ * not be in the module-accessible symbol table.
+ */
+			/* A negative sony535_irq_used will attempt an autoirq. */
+			if (sony535_irq_used < 0) {
+				autoirq_setup(0);
+				enable_interrupts();
+				outb(0, read_status_reg);	/* does a reset? */
+				sony535_irq_used = autoirq_report(10);
+				disable_interrupts();
+			}
+#endif
+			if (sony535_irq_used > 0) {
+			    if (request_irq(sony535_irq_used, cdu535_interrupt,
+								SA_INTERRUPT, CDU535_HANDLE)) {
+					printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME
+							" driver; polling instead.\n", sony535_irq_used);
+					sony535_irq_used = 0;
+				}
+			}
+			cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+			cmd_buff[1] = 0x0;	/* default audio */
+			if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
+				/* set the drive mode successful, we are set! */
+				sony_buffer_size = SONY535_BUFFER_SIZE;
+				sony_buffer_sectors = sony_buffer_size / 2048;
+
+				printk(CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
+					   drive_config.vendor_id,
+					   drive_config.product_id,
+					   drive_config.product_rev_level);
+				printk("  base address %03X, ", sony535_cd_base_io);
+				if (tmp_irq > 0)
+					printk("IRQ%d, ", tmp_irq);
+				printk("using %d byte buffer\n", sony_buffer_size);
+
+				if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
+					printk("Unable to get major %d for %s\n",
+							MAJOR_NR, CDU535_MESSAGE_NAME);
+					return -EIO;
+				}
+				blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+				read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read-ahead */
+
+				sony_toc = (struct s535_sony_toc *)
+					kmalloc(sizeof *sony_toc, GFP_KERNEL);
+				if (sony_toc == NULL)
+					return -ENOMEM;
+				last_sony_subcode = (struct s535_sony_subcode *)
+					kmalloc(sizeof *last_sony_subcode, GFP_KERNEL);
+				if (last_sony_subcode == NULL) {
+					kfree(sony_toc);
+					return -ENOMEM;
+				}
+				sony_buffer = (Byte **)
+					kmalloc(4 * sony_buffer_sectors, GFP_KERNEL);
+				if (sony_buffer == NULL) {
+					kfree(sony_toc);
+					kfree(last_sony_subcode);
+					return -ENOMEM;
+				}
+				for (i = 0; i < sony_buffer_sectors; i++) {
+					sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL);
+					if (sony_buffer[i] == NULL) {
+						while (--i>=0)
+							kfree(sony_buffer[i]);
+						kfree(sony_buffer);
+						kfree(sony_toc);
+						kfree(last_sony_subcode);
+						return -ENOMEM;
+					}
+				}
+				initialized = 1;
+			}
+		}
+	}
+
+	if (!initialized) {
+		printk("Did not find a " CDU535_MESSAGE_NAME " drive\n");
+		return -EIO;
+	}
+	request_region(sony535_cd_base_io, 4, CDU535_HANDLE);
+	return 0;
+}
+
+#ifndef MODULE
+/*
+ * accept "kernel command line" parameters
+ * (added by emoenke@gwdg.de)
+ *
+ * use: tell LILO:
+ *                 sonycd535=0x320
+ *
+ * the address value has to be the existing CDROM port address.
+ */
+void
+sonycd535_setup(char *strings, int *ints)
+{
+	/* if IRQ change and default io base desired,
+	 * then call with io base of 0
+	 */
+	if (ints[0] > 0)
+		if (ints[0] != 0)
+			sony535_cd_base_io = ints[1];
+	if (ints[0] > 1)
+		sony535_irq_used = ints[2];
+	if ((strings != NULL) && (*strings != '\0'))
+		printk(CDU535_MESSAGE_NAME
+				": Warning: Unknown interface type: %s\n", strings);
+}
+
+#else /* MODULE */
+
+void
+cleanup_module(void)
+{
+	int i;
+	if (MOD_IN_USE) {
+		printk(CDU535_HANDLE " module in use, cannot remove\n");
+		return;
+	}
+	release_region(sony535_cd_base_io, 4);
+	for (i = 0; i < sony_buffer_sectors; i++)
+		kfree_s(sony_buffer[i], 2048);
+	kfree_s(sony_buffer, 4 * sony_buffer_sectors);
+	kfree_s(last_sony_subcode, sizeof *last_sony_subcode);
+	kfree_s(sony_toc, sizeof *sony_toc);
+	if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
+		printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
+	else
+		printk(CDU535_HANDLE " module released\n");
+}
+#endif	/* MODULE */
diff -u --recursive --new-file v1.3.35/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v1.3.35/linux/drivers/char/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/Config.in	Sun Oct 22 16:03:27 1995
@@ -0,0 +1,31 @@
+#
+# Character device configuration
+#
+mainmenu_option next_comment
+comment 'character devices'
+
+bool 'Cyclades async mux support' CONFIG_CYCLADES
+bool 'Stallion multiport serial support' CONFIG_STALDRV
+if [ "$CONFIG_STALDRV" = "y" ]; then
+  tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION
+  tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
+fi
+tristate 'Parallel printer support' CONFIG_PRINTER
+tristate 'Logitech busmouse support' CONFIG_BUSMOUSE
+tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE
+if [ "$CONFIG_PSMOUSE" = "y" ]; then
+  bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE
+fi
+tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE
+tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE
+
+bool 'QIC-02 tape support' CONFIG_QIC02_TAPE
+if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
+  bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF
+  if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+    comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
+  else
+    comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
+    comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
+fi
+fi
diff -u --recursive --new-file v1.3.35/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.35/linux/drivers/net/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/Config.in	Sun Oct 22 16:03:27 1995
@@ -0,0 +1,85 @@
+#
+# Network device configuration
+#
+tristate 'Dummy net driver support' CONFIG_DUMMY
+tristate 'SLIP (serial line) support' CONFIG_SLIP
+if [ "$CONFIG_SLIP" != "n" ]; then
+  bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
+fi
+tristate 'PPP (point-to-point) support' CONFIG_PPP
+if [ ! "$CONFIG_PPP" = "n" ]; then
+  comment 'CCP compressors for PPP are only built as modules.'
+fi
+if [ "$CONFIG_AX25" = "y" ]; then
+  bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
+else
+  bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
+fi
+tristate 'PLIP (parallel port) support' CONFIG_PLIP
+tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
+bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA
+bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
+if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
+  tristate 'WD80*3 support' CONFIG_WD80x3
+  tristate 'SMC Ultra support' CONFIG_ULTRA
+fi
+bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
+bool '3COM cards' CONFIG_NET_VENDOR_3COM
+if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
+  tristate '3c501 support' CONFIG_EL1
+  tristate '3c503 support' CONFIG_EL2
+  if [ "$CONFIG_NET_ALPHA" = "y" ]; then
+    tristate '3c505 support' CONFIG_ELPLUS
+    tristate '3c507 support' CONFIG_EL16
+  fi
+  tristate '3c509/3c579 support' CONFIG_EL3
+fi
+bool 'Other ISA cards' CONFIG_NET_ISA
+if [ "$CONFIG_NET_ISA" = "y" ]; then
+  tristate 'Cabletron E21xx support' CONFIG_E2100
+  tristate 'DEPCA support' CONFIG_DEPCA
+  tristate 'EtherWorks 3 support' CONFIG_EWRK3
+  if [ "$CONFIG_NET_ALPHA" = "y" ]; then
+    bool 'SEEQ8005 support' CONFIG_SEEQ8005
+    tristate 'AT1700 support' CONFIG_AT1700
+    tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO
+    tristate 'EtherExpress support' CONFIG_EEXPRESS
+    bool 'NI5210 support' CONFIG_NI52
+    bool 'NI6510 support' CONFIG_NI65
+    tristate 'WaveLAN support' CONFIG_WAVELAN
+  fi
+  tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS
+  tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN
+  tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100
+  tristate 'NE2000/NE1000 support' CONFIG_NE2000
+  if [ "$CONFIG_AX25" = "y" ]; then
+    bool 'Ottawa PI and PI/2 support' CONFIG_PI
+  fi
+  bool 'SK_G16 support' CONFIG_SK_G16
+fi
+bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA
+if [ "$CONFIG_NET_EISA" = "y" ]; then
+  if [ "$CONFIG_NET_ALPHA" = "y" ]; then
+    tristate 'Ansel Communications EISA 3200 support' CONFIG_AC3200
+  fi
+  tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
+  tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5
+# tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP
+# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100
+# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32
+  bool 'Zenith Z-Note support' CONFIG_ZNET
+fi
+bool 'Pocket and portable adaptors' CONFIG_NET_POCKET
+if [ "$CONFIG_NET_POCKET" = "y" ]; then
+  bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP
+  tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600
+  tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620
+# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA
+# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN
+# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589
+fi
+bool 'Token Ring driver support' CONFIG_TR
+if [ "$CONFIG_TR" = "y" ]; then
+  tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR
+fi
+tristate 'Arcnet support' CONFIG_ARCNET
diff -u --recursive --new-file v1.3.35/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v1.3.35/linux/drivers/net/eql.c	Wed Oct  4 14:14:30 1995
+++ linux/drivers/net/eql.c	Mon Oct 23 08:51:10 1995
@@ -854,26 +854,26 @@
 eql_remove_slave(slave_queue_t *queue, slave_t *slave)
 {
   slave_t *prev;
-  slave_t *current;
+  slave_t *curr;
 
   cli ();
 
   prev = queue->head;
-  current = queue->head->next;
-  while (current != slave && 
-	 current->dev != 0 )
+  curr = queue->head->next;
+  while (curr != slave && 
+	 curr->dev != 0 )
     {
 /* printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */
-      prev = current;
-      current = current->next;
+      prev = curr;
+      curr = curr->next;
     }
 
-  if (current == slave)
+  if (curr == slave)
     {
-      prev->next = current->next;
+      prev->next = curr->next;
       queue->num_slaves--;
       sti();
-      return current;
+      return curr;
     }
 
   sti ();
@@ -914,7 +914,7 @@
 eql_remove_slave_dev(slave_queue_t *queue, struct device *dev)
 {
   slave_t *prev;
-  slave_t *current;
+  slave_t *curr;
   slave_t *target;
 
   target = eql_find_slave_dev (queue, dev);
@@ -924,18 +924,18 @@
       cli ();
 
       prev = queue->head;
-      current = prev->next;
-      while (current != target)
+      curr = prev->next;
+      while (curr != target)
 	{
-	  prev = current;
-	  current = current->next;
+	  prev = curr;
+	  curr = curr->next;
 	}
-      prev->next = current->next;
+      prev->next = curr->next;
       queue->num_slaves--;
 
       sti ();
 
-      eql_delete_slave (current);
+      eql_delete_slave (curr);
       return 0;
     }
   return 1;
diff -u --recursive --new-file v1.3.35/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.3.35/linux/drivers/net/ppp.c	Mon Oct 16 18:38:23 1995
+++ linux/drivers/net/ppp.c	Fri Oct 20 10:10:39 1995
@@ -6,7 +6,7 @@
  *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
  *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
  *
- *  ==FILEVERSION 4==
+ *  ==FILEVERSION 6==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -70,9 +70,9 @@
 #include <linux/module.h>
 #ifdef MODULE
 #include <linux/version.h>
-#define STATIC
+#define  STATIC
 #else
-#define STATIC static
+#define  STATIC static
 #endif /* def MODULE */
 
 #include <endian.h>
@@ -554,9 +554,11 @@
 	if (first_time) {
 		static struct symbol_table ppp_syms = {
 #include <linux/symtab_begin.h>
-			X(ppp_register_compressor),
-			X(ppp_unregister_compressor),
-			X(ppp_crc16_table),
+#define Y(sym) { (void *) &sym, SYMBOL_NAME_STR (sym) }
+			Y(ppp_register_compressor),
+			Y(ppp_unregister_compressor),
+			Y(ppp_crc16_table),
+#undef Y
 #include <linux/symtab_end.h>
 		};
 
diff -u --recursive --new-file v1.3.35/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v1.3.35/linux/drivers/pci/pci.c	Wed Sep 27 15:59:58 1995
+++ linux/drivers/pci/pci.c	Mon Oct 23 13:21:52 1995
@@ -59,7 +59,7 @@
 	DEVICE( TSENG,		TSENG_W32P_d,	"ET4000W32P rev D"),
 	DEVICE( WEITEK,		WEITEK_P9000,	"P9000"),
 	DEVICE( WEITEK,		WEITEK_P9100,	"P9100"),
-	DEVICE( DEC,		DEC_BRD,	"DC21050"),
+	BRIDGE( DEC,		DEC_BRD,	"DC21050", 		0x00),
 	DEVICE( DEC,		DEC_TULIP,	"DC21040"),
 	DEVICE( DEC,		DEC_TULIP_FAST,	"DC21140"),
 	DEVICE( DEC,		DEC_FDDI,	"DEFPA"),
@@ -161,6 +161,7 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_294x,	"294x"),
 	DEVICE( ADAPTEC,	ADAPTEC_2940,	"2940"),
+	DEVICE( ADAPTEC,	ADAPTEC_7872,	"AIC-7872"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
 	DEVICE( HER,		HER_STING,	"Stingray"),
 	DEVICE( HER,		HER_STINGARK,	"Stingray ARK 2000PV")
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- v1.3.35/linux/drivers/scsi/53c7,8xx.c	Wed Oct  4 14:14:32 1995
+++ linux/drivers/scsi/53c7,8xx.c	Sat Oct 21 19:43:31 1995
@@ -172,7 +172,7 @@
 #include <linux/proc_fs.h>
 #include <linux/string.h>
 #include <linux/mm.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "53c7,8xx.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.35/linux/drivers/scsi/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/Config.in	Sun Oct 22 16:02:13 1995
@@ -0,0 +1,39 @@
+comment 'SCSI support type (disk, tape, CDrom)'
+
+dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI
+dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
+dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
+dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI
+
+comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
+
+bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN
+
+bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS
+
+mainmenu_option next_comment
+comment 'SCSI low-level drivers'
+
+dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
+dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
+dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
+dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
+dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
+dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
+dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
+dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
+bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380
+if [ "$CONFIG_PCI" = "y" ]; then
+  dep_tristate 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI
+fi
+dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 $CONFIG_SCSI
+bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16
+dep_tristate 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC $CONFIG_SCSI
+dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
+bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128
+dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
+dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
+dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI
+dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
+#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/NCR53c406a.c linux/drivers/scsi/NCR53c406a.c
--- v1.3.35/linux/drivers/scsi/NCR53c406a.c	Wed Sep 27 15:59:58 1995
+++ linux/drivers/scsi/NCR53c406a.c	Sat Oct 21 19:43:31 1995
@@ -51,7 +51,7 @@
 #include <asm/bitops.h>
 #include <asm/irq.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v1.3.35/linux/drivers/scsi/aha152x.c	Wed Sep 27 15:59:59 1995
+++ linux/drivers/scsi/aha152x.c	Sat Oct 21 19:43:31 1995
@@ -215,7 +215,7 @@
 
 #include <linux/sched.h>
 #include <asm/io.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "sd.h"
 #include "hosts.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h
--- v1.3.35/linux/drivers/scsi/aha152x.h	Mon Sep 18 14:53:55 1995
+++ linux/drivers/scsi/aha152x.h	Sat Oct 21 19:51:03 1995
@@ -7,7 +7,7 @@
 
 #if defined(__KERNEL__)
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include <asm/io.h>
 
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v1.3.35/linux/drivers/scsi/aha1542.c	Mon Oct 16 18:38:23 1995
+++ linux/drivers/scsi/aha1542.c	Sat Oct 21 19:43:31 1995
@@ -31,7 +31,7 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/io.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha1740.c linux/drivers/scsi/aha1740.c
--- v1.3.35/linux/drivers/scsi/aha1740.c	Mon Sep 18 14:53:55 1995
+++ linux/drivers/scsi/aha1740.c	Sat Oct 21 19:43:31 1995
@@ -32,7 +32,7 @@
 
 #include <asm/system.h>
 #include <asm/io.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v1.3.35/linux/drivers/scsi/aic7xxx.c	Mon Sep 18 14:53:56 1995
+++ linux/drivers/scsi/aic7xxx.c	Mon Oct 23 13:21:53 1995
@@ -41,7 +41,7 @@
  *
  *    -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 04/03/95
  *
- *  $Id: aic7xxx.c,v 2.0 1995/08/02 05:28:42 deang Exp $
+ *  $Id: aic7xxx.c,v 2.5 1995/09/20 05:18:18 deang Exp $
  *-M*************************************************************************/
 
 #ifdef MODULE
@@ -59,7 +59,7 @@
 #include <linux/sched.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "sd.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -71,10 +71,11 @@
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 2.0 $"
+#define AIC7XXX_C_VERSION  "$Revision: 2.5 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        ((a < b) ? a : b)
+#define ALL_TARGETS -1
 #ifndef TRUE
 #  define TRUE 1
 #endif
@@ -84,7 +85,8 @@
 
 /*
  * Defines for PCI bus support, testing twin bus support, DMAing of
- * SCBs, and tagged queueing.
+ * SCBs, tagged queueing, commands (SCBs) per lun, and SCSI bus reset
+ * delay time.
  *
  *   o PCI bus support - this has been implemented and working since
  *     the December 1, 1994 release of this driver. If you don't have
@@ -95,7 +97,9 @@
  *
  *   o Twin bus support - this has been tested and does work.
  *
- *   o DMAing of SCBs - thanks to Kai Makisara, this now works
+ *   o DMAing of SCBs - thanks to Kai Makisara, this now works.
+ *     This define is now taken out and DMAing of SCBs is always
+ *     performed (8/12/95 - DE).
  *
  *   o Tagged queueing - this driver is capable of tagged queueing
  *     but I am unsure as to how well the higher level driver implements
@@ -109,29 +113,55 @@
  *     PCI code and interrupt handling needs to be modified to
  *     support this.
  *
+ *   o Commands per lun - If tagged queueing is enabled, then you
+ *     may want to try increasing AIC7XXX_CMDS_PER_LUN to more
+ *     than 2.  By default, we limit the SCBs per lun to 2 with
+ *     or without tagged queueing enabled.  If tagged queueing is
+ *     disabled, the sequencer will keep the 2nd SCB in the input
+ *     queue until the first one completes - so it is OK to to have
+ *     more than 1 SCB queued.  If tagged queueing is enabled, then
+ *     the sequencer will attempt to send the 2nd SCB to the device
+ *     while the first SCB is executing and the device is disconnected.
+ *     For adapters limited to 4 SCBs, you may want to actually
+ *     decrease the commands per lun to 1, if you often have more
+ *     than 2 devices active at the same time.  This will allocate
+ *     1 SCB for each device and ensure that there will always be
+ *     a free SCB for up to 4 devices active at the same time.
+ *
  *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 03/11/95
  */
 
 /* Uncomment this for testing twin bus support. */
 #define AIC7XXX_TWIN_SUPPORT
 
-/* Uncomment this for DMAing of SCBs. */
-#define AIC7XXX_USE_DMA
-
 /* Uncomment this for tagged queueing. */
 /* #define AIC7XXX_TAGGED_QUEUEING */
 
 /* Uncomment this for allowing sharing of IRQs. */
 #define AIC7XXX_SHARE_IRQS
 
+/*
+ * You can try raising me if tagged queueing is enabled, or lowering
+ * me if you only have 4 SCBs.
+ */
+#define AIC7XXX_CMDS_PER_LUN 2
+
 /* Set this to the delay in seconds after SCSI bus reset. */
 #define AIC7XXX_RESET_DELAY 15
 
 /*
- * Uncomment this to always use scatter/gather lists.
- * *NOTE: The sequencer must be changed also!
+ * Uncomment the following define for collection of SCSI transfer statistics
+ * for the /proc filesystem.
+ *
+ * NOTE: This does affect performance since it has to maintain statistics.
+ */
+/* #define AIC7XXX_PROC_STATS */
+
+/*
+ * Define this to use polling rather than using kernel support for waking
+ * up a waiting process.
  */
-#define AIC7XXX_USE_SG
+#undef AIC7XXX_POLL
 
 /*
  * Controller type and options
@@ -157,6 +187,12 @@
   AIC_DISABLED
 } aha_status_type;
 
+typedef enum {
+  LIST_HEAD,
+  LIST_SECOND,
+  LIST_TAIL
+} insert_type;
+
 /*
  * There should be a specific return value for this in scsi.h, but
  * it seems that most drivers ignore it.
@@ -256,6 +292,9 @@
  * SCSI Rate
  */
 #define SCSIRATE(x)		((x) + 0xC04ul)
+#define WIDEXFER		0x80		/* Wide transfer control */
+#define SXFR			0x70		/* Sync transfer rate */
+#define SOFS			0x0F		/* Sync offset */
 
 /*
  * SCSI ID (p. 3-18).
@@ -267,6 +306,15 @@
 #define		OID		0x0F		/* Our ID mask */
 
 /*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transfered
+ * across the SCSI bus.  The counter is decremented only once
+ * the data has been safely transfered.  SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */
+#define STCNT(x)		((x) + 0xC08ul)
+
+/*
  * SCSI Status 0 (p. 3-21)
  * Contains one set of SCSI Interrupt codes
  * These are most likely of interest to the sequencer
@@ -325,6 +373,16 @@
 #define		ENREQINIT	0x01
 
 /*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transfered on the SCSI bus.  They are counted up in the same
+ * manner as STCNT is counted down.  SHADDR should always be used
+ * to determine the address of the last byte transfered since HADDR
+ * can be squewed by write ahead.
+ */
+#define	SHADDR(x)		((x) + 0xC14ul)
+
+/*
  * Selection/Reselection ID (p. 3-31)
  * Upper four bits are the device id. The ONEBIT is set when the re/selecting
  * device did not set its own ID.
@@ -404,6 +462,7 @@
 #define SEQADDR1(x)		((x) + 0xC63ul)
 
 #define ACCUM(x)		((x) + 0xC64ul)		/* accumulator */
+#define SINDEX(x)		((x) + 0xC65ul)
 
 /*
  * Board Control (p. 3-43)
@@ -448,6 +507,13 @@
 #define		UNPAUSE_294X	IRQMS | INTEN
 
 /*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transfered across the host bus.
+ */
+#define HADDR(x)		((x) + 0xC88ul)
+
+/*
  * SCB Pointer (p. 3-49)
  * Gate one of the four SCBs into the SCBARRAY window.
  */
@@ -469,7 +535,8 @@
 #define			BAD_STATUS	0x70
 #define			RESIDUAL	0x80
 #define			ABORT_TAG	0x90
-#define			AWAITING_MSG	0xa0
+#define			AWAITING_MSG	0xA0
+#define			IMMEDDONE	0xB0
 #define 	BRKADRINT 0x08
 #define		SCSIINT	  0x04
 #define		CMDCMPLT  0x02
@@ -562,7 +629,7 @@
 /*
  * Bit vector of targets that have disconnection disabled.
  */
-#define	HA_DISC_DSB		((x) + 0xc32ul)
+#define	HA_DISC_DSB(x)		((x) + 0xC32ul)
 
 /*
  * Length of pending message
@@ -586,6 +653,8 @@
 #define		SEND_WDTR 	0x80
 #define		SEND_REJ	0x40
 
+#define	SG_COUNT(x)		((x) + 0xC4Dul)
+#define	SG_NEXT(x)		((x) + 0xC4Eul)
 #define HA_SIGSTATE(x)		((x) + 0xC4Bul)	/* value in SCSISIGO */
 #define HA_SCBCOUNT(x)		((x) + 0xC52ul)	/* number of hardware SCBs */
 
@@ -607,8 +676,12 @@
 #define HA_INTDEF(x)		((x) + 0xC5Cul)	/* interrupt def'n register */
 #define HA_HOSTCONF(x)		((x) + 0xC5Dul)	/* host config def'n register */
 
+#define HA_274_BIOSCTRL(x)	((x) + 0xC5Ful) /* BIOS enabled for 274x */
+#define BIOSMODE		0x30
+#define BIOSDISABLED		0x30
+
 #define MSG_ABORT		0x06
-#define	MSG_BUS_DEVICE_RESET	0x0c
+#define	MSG_BUS_DEVICE_RESET	0x0C
 #define BUS_8_BIT		0x00
 #define BUS_16_BIT		0x01
 #define BUS_32_BIT		0x02
@@ -734,8 +807,8 @@
  * kernel structure hasn't changed.
  */
 #define SG_STRUCT_CHECK(sg) \
-  ((char *)&(sg).address - (char *)&(sg) != 0 ||  \
-   (char *)&(sg).length  - (char *)&(sg) != 8 ||  \
+  ((char *) &(sg).address - (char *) &(sg) != 0 ||  \
+   (char *) &(sg).length  - (char *) &(sg) != 8 ||  \
    sizeof((sg).address) != 4 ||                   \
    sizeof((sg).length)  != 4 ||                   \
    sizeof(sg)           != 12)
@@ -786,36 +859,24 @@
 /*
  * The driver keeps up to four scb structures per card in memory. Only the
  * first 26 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookeeping. The driver is further optimized
- * so that we only have to download the first 19 bytes since as long
- * as we always use S/G, the last fields should be zero anyway.
+ * for driver level bookeeping.
  */
-#ifdef AIC7XXX_USE_SG
-#define SCB_DOWNLOAD_SIZE	19	/* amount to actually download */
-#else
-#define SCB_DOWNLOAD_SIZE	26
-#endif
-
-#define SCB_UPLOAD_SIZE		19	/* amount to actually upload */
+#define SCB_DOWNLOAD_SIZE	26	/* amount to actually download */
+#define SCB_UPLOAD_SIZE		26	/* amount to actually upload */
 
 struct aic7xxx_scb {
 /* ------------    Begin hardware supported fields    ---------------- */
 /*1 */  unsigned char control;
-#define SCB_NEEDWDTR 0x80                       /* Initiate Wide Negotiation */
-#define SCB_NEEDSDTR 0x40                       /* Initiate Sync Negotiation */
-#define SCB_NEEDDMA  0x08                       /* SCB needs to be DMA'd from
-						 * from host memory
-						 */
-#define SCB_REJ_MDP      0x80                   /* Reject MDP message */
-#define SCB_DISEN        0x40                   /* SCB Disconnect enable */
-#define SCB_TE           0x20                   /* Tag enable */
-/*      RESERVED         0x10 */
-#define SCB_WAITING      0x08                   /* Waiting */
-#define SCB_DIS          0x04                   /* Disconnected */
-#define SCB_TAG_TYPE     0x03
-#define         SIMPLE_QUEUE 0x00               /* Simple Queue */
-#define         HEAD_QUEUE   0x01               /* Head of Queue */
-#define         ORD_QUEUE    0x02               /* Ordered Queue */
+#define	SCB_NEEDWDTR 0x80			/* Initiate Wide Negotiation */
+#define SCB_DISCENB  0x40			/* Disconnection Enable */
+#define	SCB_TE	     0x20			/* Tag enable */
+#define SCB_NEEDSDTR 0x10			/* Initiate Sync Negotiation */
+#define	SCB_NEEDDMA  0x08			/* Refresh SCB from host ram */
+#define	SCB_DIS	     0x04
+#define	SCB_TAG_TYPE 0x03
+#define		SIMPLE_QUEUE	0x00
+#define		HEAD_QUEUE	0x01
+#define		OR_QUEUE	0x02
 /*              ILLEGAL      0x03 */
 /*2 */  unsigned char target_channel_lun;       /* 4/1/3 bits */
 /*3 */  unsigned char SG_segment_count;
@@ -830,7 +891,7 @@
 /*26*/  unsigned char data_count[3];
 /*30*/  unsigned char host_scb[4] __attribute__ ((packed));
 /*31*/  u_char next_waiting;            /* Used to thread SCBs awaiting selection. */
-#define SCB_LIST_NULL 0x10              /* SCB list equivelent to NULL */
+#define SCB_LIST_NULL 0xFF              /* SCB list equivelent to NULL */
 #if 0
 	/*
 	 *  No real point in transferring this to the
@@ -850,13 +911,22 @@
 #define SCB_IMMED              0x08
 #define SCB_SENSE              0x10
 	unsigned int        position;       /* Position in scb array */
-#ifdef AIC7XXX_USE_SG
 	struct scatterlist  sg;
 	struct scatterlist  sense_sg;
-#endif
 	unsigned char       sense_cmd[6];   /* Allocate 6 characters for sense command */
+#define TIMER_ENABLED		0x01
+#define TIMER_EXPIRED		0x02
+#define TIMED_CMD_DONE		0x04
+        volatile unsigned char timer_status;
+#ifndef AIC7XXX_POLL
+	struct wait_queue   *waiting;       /* wait queue for device reset command */
+        struct wait_queue   waitq;          /* waiting points to this */
+	struct timer_list   timer;          /* timeout for device reset command */
+#endif
 };
 
+typedef void (*timeout_fn)(unsigned long);
+
 static struct {
   unsigned char errno;
   const char *errmesg;
@@ -898,11 +968,34 @@
   volatile unsigned short  needwdtr_copy;    /* default config */
   volatile unsigned short  needwdtr;
   volatile unsigned short  wdtr_pending;
+  volatile unsigned short  discenable;       /* Targets allowed to disconnect */
   struct seeprom_config    seeprom;
   int                      have_seeprom;
   struct Scsi_Host        *next;             /* allow for multiple IRQs */
   struct aic7xxx_scb       scb_array[AIC7XXX_MAXSCB];  /* active commands */
   struct aic7xxx_scb      *free_scb;         /* list of free SCBs */
+#ifdef AIC7XXX_PROC_STATS
+  /*
+   * Statistics Kept:
+   *
+   * Total Xfers (count for each command that has a data xfer),
+   * broken down further by reads && writes.
+   *
+   * Binned sizes, writes && reads:
+   *    < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
+   *
+   * Total amounts read/written above 512 bytes (amts under ignored)
+   */
+  struct aic7xxx_xferstats {
+    long xfers;                              /* total xfer count */
+    long w_total;                            /* total writes */
+    long w_total512;                         /* 512 byte blocks written */
+    long w_bins[10];                         /* binned write */
+    long r_total;                            /* total reads */
+    long r_total512;                         /* 512 byte blocks read */
+    long r_bins[10];                         /* binned reads */
+  } stats[2][16][8];                         /* channel, target, lun */
+#endif /* AIC7XXX_PROC_STATS */
 };
 
 struct aic7xxx_host_config {
@@ -982,11 +1075,11 @@
    */
   if ((p->type == AIC_274x) || (p->type == AIC_284x))
   {
-    dfthresh = host_conf >> 6;
+    dfthresh = (host_conf >> 6);
   }
   else
   {
-    dfthresh = scsi_conf >> 6;
+    dfthresh = (scsi_conf >> 6);
   }
 
   brelease = p->busrtime;
@@ -1037,7 +1130,10 @@
 	 (scsi_conf & 0x40) ? "en" : "dis");
 
   if (((p->type == AIC_274x) || (p->type == AIC_284x)) && p->parity == AIC_UNKNOWN)
-  { /* Set the parity for 7770 based cards. */
+  {
+    /*
+     * Set the parity for 7770 based cards.
+     */
     p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
   }
   if (p->parity != AIC_UNKNOWN)
@@ -1061,9 +1157,35 @@
 	  (p->high_term == AIC_ENABLED) ? "en" : "dis");
   }
 }
+
+static void
+debug_scb(struct aic7xxx_scb *scb)
+{
+  printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
+         scb->control, scb->target_channel_lun, scb->SG_segment_count,
+         (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
+         (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
+         (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
+         (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
+         scb->SCSI_cmd_length);
+  printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
+         (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
+         scb->residual_SG_segment_count, (scb->residual_data_count[2] << 16) |
+         (scb->residual_data_count[1] << 8) | scb->residual_data_count[0]);
+  printk("data ptr 0x%x, data count %d, host scb 0x%x, next waiting %d\n",
+         (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
+         (scb->data_pointer[1] << 8) | scb->data_pointer[0],
+         (scb->data_count[2] << 16) | (scb->data_count[1] << 8) | scb->data_count[0],
+         (unsigned int) scb->host_scb, scb->next_waiting);
+  printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
+         (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
+         scb->position);
+}
+
 #else
 #  define debug(fmt, args...)
 #  define debug_config(x)
+#  define debug_scb(x)
 #endif AIC7XXX_DEBUG
 
 /*
@@ -1189,6 +1311,109 @@
   }
 }
 
+#ifdef AIC7XXX_POLL
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_poll_scb
+ *
+ * Description:
+ *   Function to poll for command completion when in aborting an SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_poll_scb(struct aic7xxx_host *p,
+                 struct aic7xxx_scb  *scb,
+                 unsigned long       timeout_ticks)
+{
+  unsigned long timer_expiration = jiffies + timeout_ticks;
+
+  while ((jiffies < timer_expiration) && !(scb->timer_status & TIMED_CMD_DONE))
+  {
+    udelay(1000);  /* delay for 1 msec. */
+  }
+}
+
+#else
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scb_timeout
+ *
+ * Description:
+ *   Called when a SCB reset command times out.  The input is actually
+ *   a pointer to the SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_scb_timeout(unsigned long data)
+{
+  struct aic7xxx_scb *scb = (struct aic7xxx_scb *) data;
+
+  scb->timer_status |= TIMER_EXPIRED;
+  wake_up(&(scb->waiting));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scb_untimeout
+ *
+ * Description:
+ *   This function clears the timeout and wakes up a waiting SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_scb_untimeout(struct aic7xxx_scb *scb)
+{
+  if (scb->timer_status & TIMER_ENABLED)
+  {
+    scb->timer_status = TIMED_CMD_DONE;
+    wake_up(&(scb->waiting));
+  }
+}
+#endif
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scb_tsleep
+ *
+ * Description:
+ *   Emulates a BSD tsleep where a process can sleep for a specified
+ *   amount of time, but may be awakened before that.  Linux provides
+ *   a sleep_on, wake_up, add_timer, and del_timer which can be used to
+ *   emulate tsleep, but there's not enough information available on
+ *   how to use them.  For now, we'll just poll for SCB completion.
+ *
+ *   The parameter ticks is the number of clock ticks
+ *   to wait before a timeout.  A 0 is returned if the scb does not
+ *   timeout, 1 is returned for a timeout.
+ *-F*************************************************************************/
+static int
+aic7xxx_scb_tsleep(struct aic7xxx_host *p,
+                   struct aic7xxx_scb  *scb,
+                   unsigned long        ticks)
+{
+  scb->timer_status = TIMER_ENABLED;
+#ifdef AIC7XXX_POLL
+  UNPAUSE_SEQUENCER(p);
+  aic7xxx_poll_scb(p, scb, ticks);
+#else
+  scb->waiting = &(scb->waitq);
+  scb->timer.expires = jiffies + ticks;
+  scb->timer.data = (unsigned long) scb;
+  scb->timer.function = (timeout_fn) aic7xxx_scb_timeout;
+  add_timer(&scb->timer);
+  UNPAUSE_SEQUENCER(p);
+  sleep_on(&(scb->waiting));
+  del_timer(&scb->timer);
+#endif
+  if (!(scb->timer_status & TIMED_CMD_DONE))
+  {
+    scb->timer_status = 0x0;
+    return (1);
+  }
+  else
+  {
+    scb->timer_status = 0x0;
+    return (0);
+  }
+}
+
 /*+F*************************************************************************
  * Function:
  *   rcs_version
@@ -1265,6 +1490,73 @@
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_length
+ *
+ * Description:
+ *   How much data should be transferred for this SCSI command? Stop
+ *   at segment sg_last if it's a scatter-gather command so we can
+ *   compute underflow easily.
+ *-F*************************************************************************/
+static unsigned
+aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+{
+  int i, segments;
+  unsigned length;
+  struct scatterlist *sg;
+
+  segments = cmd->use_sg - sg_last;
+  sg = (struct scatterlist *) cmd->buffer;
+
+  if (cmd->use_sg)
+  {
+    for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+    {
+      length += sg[i].length;
+    }
+  }
+  else
+  {
+    length = cmd->request_bufflen;
+  }
+
+  return (length);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scsirate
+ *
+ * Description:
+ *   Look up the valid period to SCSIRATE conversion in our table
+ *-F*************************************************************************/
+static void
+aic7xxx_scsirate(unsigned char *scsirate, unsigned char period,
+		 unsigned char offset, int target, char channel)
+{
+  int i;
+
+  for (i = 0; i < num_aic7xxx_syncrates; i++)
+  {
+    if ((aic7xxx_syncrates[i].period - period) >= 0)
+    {
+      *scsirate = (aic7xxx_syncrates[i].rate << 4) | (offset & 0x0F);
+      printk("aic7xxx: target %d, channel %c, now synchronous at %sMb/s, "
+             "offset = 0x%x\n",
+	     target, channel, aic7xxx_syncrates[i].english, offset);
+      return;
+    }
+  }
+
+  /*
+   * Default to asyncronous transfer
+   */
+  *scsirate = 0;
+  printk("aic7xxx: target %d, channel %c, using asynchronous transfers\n",
+         target, channel);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_putscb
  *
  * Description:
@@ -1273,42 +1565,23 @@
 static void
 aic7xxx_putscb(int base, struct aic7xxx_scb *scb)
 {
-#ifdef AIC7XXX_USE_DMA
   /*
    * All we need to do, is to output the position
    * of the SCB in the SCBARRAY to the QINFIFO
    * of the host adapter.
    */
   outb(scb->position, QINFIFO(base));
-#else
-  /*
-   * By turning on the SCB auto increment, any reference
-   * to the SCB I/O space postincrements the SCB address
-   * we're looking at. So turn this on and dump the relevant
-   * portion of the SCB to the card.
-   */
-  outb(SCBAUTO, SCBCNT(base));
-
-  asm volatile("cld\n\t"
-	       "rep\n\t"
-	       "outsb"
-	       : /* no output */
-	       :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
-	       :"si", "cx", "dx");
-
-  outb(0, SCBCNT(base));
-#endif
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_putdmascb
+ *   aic7xxx_putscb_dma
  *
  * Description:
  *   DMA a SCB to the controller.
  *-F*************************************************************************/
 static void
-aic7xxx_putdmascb(int base, struct aic7xxx_scb *scb)
+aic7xxx_putscb_dma(int base, struct aic7xxx_scb *scb)
 {
   /*
    * By turning on the SCB auto increment, any reference
@@ -1355,67 +1628,467 @@
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_length
+ *   aic7xxx_match_scb
  *
  * Description:
- *   How much data should be transferred for this SCSI command? Stop
- *   at segment sg_last if it's a scatter-gather command so we can
- *   compute underflow easily.
+ *   Checks to see if an scb matches the target/channel as specified.
+ *   If target is ALL_TARGETS (-1), then we're looking for any device
+ *   on the specified channel; this happens when a channel is going
+ *   to be reset and all devices on that channel must be aborted.
  *-F*************************************************************************/
-static unsigned
-aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+static int
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
 {
-  int i, segments;
-  unsigned length;
-  struct scatterlist *sg;
+  int targ = (scb->target_channel_lun >> 4) & 0x0F;
+  char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
 
-  segments = cmd->use_sg - sg_last;
-  sg = (struct scatterlist *) cmd->buffer;
+  if (target == ALL_TARGETS)
+  {
+    return (chan == channel);
+  }
+  else
+  {
+    return ((chan == channel) && (targ == target));
+  }
+}
 
-  if (cmd->use_sg)
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_unbusy_target
+ *
+ * Description:
+ *   Set the specified target inactive.
+ *-F*************************************************************************/
+static void
+aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+{
+  unsigned char active;
+  unsigned long active_port = HA_ACTIVE0(base);
+
+  if ((target > 0x07) || (channel == 'B'))
+  {
+    /*
+     * targets on the Second channel or above id 7 store info in byte two
+     * of HA_ACTIVE
+     */
+    active_port++;
+  }
+  active = inb(active_port);
+  active &= ~(0x01 << (target & 0x07));
+  outb(active_port, active);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_done
+ *
+ * Description:
+ *   Calls the higher level scsi done function and frees the scb.
+ *-F*************************************************************************/
+static void
+aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  long flags;
+  Scsi_Cmnd *cmd = scb->cmd;
+
+  if (scb->timer_status & TIMER_ENABLED)
+  {
+#ifdef AIC7XXX_POLL
+    scb->timer_status |= TIMED_CMD_DONE;
+#else
+    aic7xxx_scb_untimeout(scb);
+#endif
+  }
+  else
+  {
+    /*
+     * This is a critical section, since we don't want the
+     * queue routine mucking with the host data.
+     */
+    save_flags(flags);
+    cli();
+
+    /*
+     * Process the command after marking the scb as free
+     * and adding it to the free list.
+     */
+    scb->state = SCB_FREE;
+    scb->next = p->free_scb;
+    p->free_scb = &(p->scb_array[scb->position]);
+    scb->cmd = NULL;
+
+    restore_flags(flags);
+
+    cmd->scsi_done(cmd);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_add_waiting_scb
+ *
+ * Description:
+ *   Add this SCB to the "waiting for selection" list.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_waiting_scb(u_long              base,
+                        struct aic7xxx_scb *scb,
+                        insert_type         where)
+{
+  unsigned char head, tail;
+  unsigned char curscb;
+
+  curscb = inb(SCBPTR(base));
+  head = inb(WAITING_SCBH(base));
+  tail = inb(WAITING_SCBT(base));
+  if (head == SCB_LIST_NULL)
+  {
+    /*
+     * List was empty
+     */
+    head = scb->position;
+    tail = SCB_LIST_NULL;
+  }
+  else
   {
-    for (i = length = 0; i < cmd->use_sg && i < segments; i++)
+    if (where == LIST_HEAD)
     {
-      length += sg[i].length;
+      outb(scb->position, SCBPTR(base));
+      outb(head, SCBARRAY(base) + 30);
+      head = scb->position;
     }
+    else
+    {
+      if (tail == SCB_LIST_NULL)
+      {
+        /*
+         * List had one element
+         */
+        tail = scb->position;
+        outb(head, SCBPTR(base));
+        outb(tail, SCBARRAY(base) + 30);
+      }
+      else
+      {
+        if (where == LIST_SECOND)
+        {
+          unsigned char third_scb;
+
+          outb(head, SCBPTR(base));
+          third_scb = inb(SCBARRAY(base) + 30);
+          outb(scb->position, SCBARRAY(base) + 30);
+          outb(scb->position, SCBPTR(base));
+          outb(third_scb, SCBARRAY(base) + 30);
+        }
+        else
+        {
+          outb(tail, SCBPTR(base));
+          tail = scb->position;
+          outb(tail, SCBARRAY(base) + 30);
+        }
+      }
+    }
+  }
+  outb(head, WAITING_SCBH(base));
+  outb(tail, WAITING_SCBT(base));
+  outb(curscb, SCBPTR(base));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_abort_waiting_scb
+ *
+ * Description:
+ *   Manipulate the waiting for selection list and return the
+ *   scb that follows the one that we remove.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+                          unsigned char prev, unsigned char timedout_scb)
+{
+  unsigned char curscb, next;
+  int target = (scb->target_channel_lun >> 4) & 0x0F;
+  char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int base = p->base;
+
+  /*
+   * Select the SCB we want to abort and
+   * pull the next pointer out of it.
+   */
+  curscb = inb(SCBPTR(base));
+  outb(scb->position, SCBPTR(base));
+  next = inb(SCBARRAY(base) + 30);
+
+  /*
+   * Clear the necessary fields
+   */
+  outb(SCB_NEEDDMA, SCBARRAY(base));
+  outb(SCB_LIST_NULL, SCBARRAY(base) + 30);
+  aic7xxx_unbusy_target(target, channel, base);
+
+  /*
+   * Update the waiting list
+   */
+  if (prev == SCB_LIST_NULL)
+  {
+    /*
+     * First in the list
+     */
+    outb(next, WAITING_SCBH(base));
   }
   else
   {
-    length = cmd->request_bufflen;
+    /*
+     * Select the scb that pointed to us and update its next pointer.
+     */
+    outb(prev, SCBPTR(base));
+    outb(next, SCBARRAY(base) + 30);
+  }
+  /*
+   * Update the tale pointer
+   */
+  if (inb(WAITING_SCBT(base)) == scb->position)
+  {
+    outb(prev, WAITING_SCBT(base));
   }
 
-  return (length);
+  /*
+   * Point us back at the original scb position
+   * and inform the SCSI system that the command
+   * has been aborted.
+   */
+  outb(curscb, SCBPTR(base));
+  scb->state |= SCB_ABORTED;
+  scb->cmd->result = (DID_RESET << 16);
+  aic7xxx_done(p, scb);
+
+  return (next);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_scsirate
+ *   aic7xxx_reset_device
  *
  * Description:
- *   Look up the valid period to SCSIRATE conversion in our table
+ *   The device at the given target/channel has been reset.  Abort
+ *   all active and queued scbs for that target/channel.
+ *-F*************************************************************************/
+static int
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+                     unsigned char timedout_scb)
+{
+  int base = p->base;
+  struct aic7xxx_scb *scb;
+  unsigned char active_scb;
+  int i = 0;
+  int found = 0;
+
+  /*
+   * Restore this when we're done
+   */
+  active_scb = inb(SCBPTR(base));
+
+  /*
+   * Search the QINFIFO.
+   */
+  {
+    int saved_queue[AIC7XXX_MAXSCB];
+    int queued = inb(QINCNT(base));
+
+    for (i = 0; i < (queued - found); i++)
+    {
+      saved_queue[i] = inb(QINFIFO(base));
+      scb = &(p->scb_array[saved_queue[i]]);
+      if (aic7xxx_match_scb(scb, target, channel))
+      {
+        /*
+         * We found an scb that needs to be aborted.
+         */
+        scb->state |= SCB_ABORTED;
+        scb->cmd->result = (DID_RESET << 16);
+        aic7xxx_done(p, scb);
+        outb(scb->position, SCBPTR(base));
+        outb(SCB_NEEDDMA, SCBARRAY(base));
+        i--;
+        found++;
+      }
+    }
+    /*
+     * Now put the saved scbs back.
+     */
+    for (queued = 0; queued < i; queued++)
+    {
+      outb(saved_queue[queued], QINFIFO(base));
+    }
+  }
+
+  /*
+   * Search waiting for selection list.
+   */
+  {
+    unsigned char next, prev;
+
+    next = inb(WAITING_SCBH(base));  /* Start at head of list. */
+    prev = SCB_LIST_NULL;
+
+    while (next != SCB_LIST_NULL)
+    {
+      scb = &(p->scb_array[next]);
+      /*
+       * Select the SCB.
+       */
+      if (aic7xxx_match_scb(scb, target, channel))
+      {
+        next = aic7xxx_abort_waiting_scb(p, scb, prev, timedout_scb);
+        found++;
+      }
+      else
+      {
+        outb(scb->position, SCBPTR(base));
+        prev = next;
+        next = inb(SCBARRAY(base) + 30);
+      }
+    }
+  }
+
+  /*
+   * Go through the entire SCB array now and look for
+   * commands for this target that are active.  These
+   * are other (most likely tagged) commands that
+   * were disconnected when the reset occured.
+   */
+  for(i = 0; i < p->numscb; i++)
+  {
+    scb = &(p->scb_array[i]);
+    if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+    {
+      /*
+       * Ensure the target is "free"
+       */
+      aic7xxx_unbusy_target(target, channel, base);
+      outb(scb->position, SCBPTR(base));
+      outb(SCB_NEEDDMA, SCBARRAY(base));
+      scb->state |= SCB_ABORTED;
+      scb->cmd->result = (DID_RESET << 16);
+      aic7xxx_done(p, scb);
+      found++;
+    }
+  }
+
+  outb(active_scb, SCBPTR(base));
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset_current_bus
+ *
+ * Description:
+ *   Reset the current SCSI bus.
  *-F*************************************************************************/
 static void
-aic7xxx_scsirate(unsigned char *scsirate, unsigned char period,
-		 unsigned char offset, int target)
+aic7xxx_reset_current_bus(int base)
 {
-  int i;
+  outb(SCSIRSTO, SCSISEQ(base));
+  udelay(1000);
+  outb(0, SCSISEQ(base));
+}
 
-  for (i = 0; i < num_aic7xxx_syncrates; i++)
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset_channel
+ *
+ * Description:
+ *   Reset the channel.
+ *-F*************************************************************************/
+static int
+aic7xxx_reset_channel(struct aic7xxx_host *p, char channel,
+                     unsigned char timedout_scb)
+{
+  int base = p->base;
+  unsigned char sblkctl;
+  char cur_channel;
+  unsigned long offset, offset_max;
+  int found;
+
+  /*
+   * Clean up all the state information for the
+   * pending transactions on this bus.
+   */
+  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, timedout_scb);
+
+  if (channel == 'B')
   {
-    if ((aic7xxx_syncrates[i].period - period) >= 0)
+    p->needsdtr |= (p->needsdtr_copy & 0xFF00);
+    p->sdtr_pending &= 0x00FF;
+    outb(0, HA_ACTIVE1(base));
+    offset = HA_TARG_SCRATCH(base) + 8;
+    offset_max = HA_TARG_SCRATCH(base) + 16;
+  }
+  else
+  {
+    if (p->bus_type == AIC_WIDE)
     {
-      *scsirate = (aic7xxx_syncrates[i].rate << 4) | (offset & 0x0F);
-      printk("aic7xxx: target %d now synchronous at %sMb/s, offset = 0x%x\n",
-	     target, aic7xxx_syncrates[i].english, offset);
-      return;
+      p->needsdtr = p->needsdtr_copy;
+      p->needwdtr = p->needwdtr_copy;
+      p->sdtr_pending = 0;
+      p->wdtr_pending = 0;
+      outb(0, HA_ACTIVE0(base));
+      outb(0, HA_ACTIVE1(base));
+      offset = HA_TARG_SCRATCH(base);
+      offset_max = HA_TARG_SCRATCH(base) + 16;
     }
+    else
+    {
+      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
+      p->sdtr_pending &= 0xFF00;
+      outb(0, HA_ACTIVE0(base));
+      offset = HA_TARG_SCRATCH(base);
+      offset_max = HA_TARG_SCRATCH(base) + 8;
+    }
+  }
+  while (offset < offset_max)
+  {
+    /*
+     * Revert to async/narrow transfers
+     * until we renegotiate.
+     */
+    u_char targ_scratch;
+    targ_scratch = inb(offset);
+    targ_scratch &= SXFR;
+    outb(targ_scratch, offset);
+    offset++;
   }
 
   /*
-   * Default to asyncronous transfer
+   * Reset the bus and unpause/restart the controller
    */
-  *scsirate = 0;
-  printk("aic7xxx: target %d using asynchronous transfers\n", target);
+
+  /*
+   * Case 1: Command for another bus is active
+   */
+  sblkctl = inb(SBLKCTL(base));
+  cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
+  if (cur_channel != channel)
+  {
+    /*
+     * Stealthily reset the other bus without upsetting the current bus
+     */
+    outb(sblkctl ^ SELBUSB, SBLKCTL(base));
+    aic7xxx_reset_current_bus(base);
+    outb(sblkctl, SBLKCTL(base));
+    UNPAUSE_SEQUENCER(p);
+  }
+  /*
+   * Case 2: A command from this bus is active or we're idle
+   */
+  else
+  {
+    aic7xxx_reset_current_bus(base);
+    RESTART_SEQUENCER(p);
+  }
+
+  return found;
 }
 
 /*+F*************************************************************************
@@ -1434,17 +2107,15 @@
   int base, intstat;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
-  unsigned char active, ha_flags, transfer;
+  unsigned char ha_flags, transfer;
   unsigned char scsi_id, bus_width;
-  unsigned char offset, rate, scratch;
+  unsigned char offset, rate, scratch, scratch_offset;
   unsigned char max_offset, rej_byte;
-  unsigned char head, tail;
-  unsigned short target_mask;
-  long flags;
+  unsigned short target_mask, active;
+  char channel;
   void *addr;
   int actual;
-  int target, tcl;
-  int scbptr;
+  int scb_index;
   Scsi_Cmnd *cmd;
 
   p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
@@ -1483,7 +2154,10 @@
     }
   }
 
-  p->isr_count++; /* Keep track of interrupts for /proc/scsi */
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
 
   if ((p->a_scanned == 0) && (p->isr_count == 1))
   {
@@ -1519,20 +2193,28 @@
     }
 
     panic("aic7xxx_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
-	  inb(ERROR(base)),
-	  inb(SEQADDR1(base)) << 8 | inb(SEQADDR0(base)));
+	  inb(ERROR(base)), (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base)));
   }
 
   if (intstat & SEQINT)
   {
     /*
      * Although the sequencer is paused immediately on
-     * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
-     * condition will have unpaused the sequencer before
-     * this point.
+     * a SEQINT, an interrupt for a SCSIINT condition will
+     * unpaused the sequencer before this point.
      */
     PAUSE_SEQUENCER(p);
 
+    scsi_id = (inb(SCSIID(base)) >> 4) & 0x0F;
+    scratch_offset = scsi_id;
+    channel = 'A';
+    if (inb(SBLKCTL(base)) & SELBUSB)
+    {
+      channel = 'B';
+      scratch_offset += 8;
+    }
+    target_mask = (0x01 << scratch_offset);
+
     switch (intstat & SEQINT_MASK)
     {
       case BAD_PHASE:
@@ -1541,9 +2223,6 @@
 
       case SEND_REJECT:
         rej_byte = inb(HA_REJBYTE(base));
-        scsi_id = inb(SCSIID(base)) >> 0x04;
-        scbptr = inb(SCBPTR(base));
-        scb = &(p->scb_array[scbptr]);
         if (rej_byte != 0x20)
         {
           debug("aic7xxx_isr warning: issuing message reject, 1st byte 0x%x\n",
@@ -1551,53 +2230,28 @@
         }
         else
         {
+          scb_index = inb(SCBPTR(base));
+          scb = &(p->scb_array[scb_index]);
           printk("aic7xxx_isr warning: Tagged message rejected for target %d,"
-                 " channel %c.\n",
-                 scsi_id, (inb(SBLKCTL(base)) & SELBUSB ? 'B': 'A'));
+                 " channel %c.\n", scsi_id, channel);
           scb->cmd->device->tagged_supported = 0;
           scb->cmd->device->tagged_queue = 0;
         }
 	break;
 
       case NO_IDENT:
-	panic("aic7xxx_isr: reconnecting target %d at seqaddr 0x%x "
-	      "didn't issue IDENTIFY message\n",
-	      (inb(SELID(base)) >> 4) & 0x0F,
-	      (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base)));
+	panic("aic7xxx_isr: Target %d, channel %c, did not send an IDENTIFY "
+	      "message.  SAVED_TCL = 0x%x\n",
+              scsi_id, channel, inb(SAVED_TCL(base)));
 	break;
 
       case NO_MATCH:
-	tcl = inb(SCBARRAY(base) + 1);
-	target = (tcl >> 4) & 0x0F;
-	/* Purposefully mask off the top bit of targets 8-15. */
-	target_mask = 0x01 << (target & 0x07);
-
-	debug("aic7xxx_isr: sequencer couldn't find match "
-	      "for reconnecting target %d, channel %d, lun %d - "
-	      "issuing ABORT\n", target, (tcl & 0x08) >> 3, tcl & 0x07);
-	if (tcl & 0x88)
-	{
-	  /* Second channel stores its info in byte
-	   * two of HA_ACTIVE
-	   */
-	  active = inb(HA_ACTIVE1(base));
-	  active = active & ~(target_mask);
-	  outb(active, HA_ACTIVE1(base));
-	}
-	else
-	{
-	  active = inb(HA_ACTIVE0(base));
-	  active = active & ~(target_mask);
-	  outb(active, HA_ACTIVE0(base));
-	}
-#ifdef AIC7XXX_USE_DMA
-	outb(SCB_NEEDDMA, SCBARRAY(base));
-#endif
+	printk("aic7xxx_isr: No active SCB for reconnecting target %d, "
+	      "channel %c - issuing ABORT\n", scsi_id, channel);
+        printk("SAVED_TCL = 0x%x\n", inb(SAVED_TCL(base)));
+        aic7xxx_unbusy_target(scsi_id, channel, base);
+        outb(SCB_NEEDDMA, SCBARRAY(base));
 
-	/*
-	 * Check out why this use to be outb(0x80, CLRINT(base))
-	 * clear the timeout
-	 */
 	outb(CLRSELTIMEO, CLRSINT1(base));
 	RESTART_SEQUENCER(p);
 	break;
@@ -1611,33 +2265,27 @@
 	 */
 	transfer = (inb(HA_ARG_1(base)) << 2);
 	offset = inb(ACCUM(base));
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	if (inb(SBLKCTL(base)) & 0x08)
-	{
-	  scsi_id = scsi_id + 8;  /* B channel */
-	}
-	target_mask = (0x01 << scsi_id);
-	scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
+	scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
 	/*
 	 * The maximum offset for a wide device is 0x08; for a
-	 * 8-bit bus device the maximum offset is 0x0f.
+	 * 8-bit bus device the maximum offset is 0x0F.
 	 */
-	if (scratch & 0x80)
+	if (scratch & WIDEXFER)
 	{
 	  max_offset = 0x08;
 	}
 	else
 	{
-	  max_offset = 0x0f;
+	  max_offset = 0x0F;
 	}
-	aic7xxx_scsirate(&rate, transfer, MIN(offset, max_offset), scsi_id);
+	aic7xxx_scsirate(&rate, transfer, MIN(offset, max_offset), scsi_id, channel);
 	/*
 	 * Preserve the wide transfer flag.
 	 */
-	rate = rate | (scratch & 0x80);
-	outb(rate, HA_TARG_SCRATCH(base) + scsi_id);
-	outb(rate, SCSIRATE(base));
-	if ((rate & 0xf) == 0)
+	scratch = rate | (scratch & WIDEXFER);
+	outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	outb(scratch, SCSIRATE(base));
+	if ((scratch & 0x0F) == 0)
 	{ /*
 	   * The requested rate was so low that asynchronous transfers
 	   * are faster (not to mention the controller won't support
@@ -1670,23 +2318,23 @@
 	/*
 	 * Clear the flags.
 	 */
-	p->needsdtr = p->needsdtr & ~target_mask;
-	p->sdtr_pending = p->sdtr_pending & ~target_mask;
+	p->needsdtr &= ~target_mask;
+	p->sdtr_pending &= ~target_mask;
+#if 0
+  scb_index = inb(SCBPTR(base));
+  scb = &(p->scb_array[scb_index]);
+  debug_scb(scb);
+#endif
+
 	break;
 
       case MSG_WDTR:
       {
 	bus_width = inb(ACCUM(base));
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	if (inb(SBLKCTL(base)) & 0x08)
-	{
-	  scsi_id = scsi_id + 8;  /* B channel */
-	}
-	printk("aic7xxx_isr: Received MSG_WDTR, scsi_id = %d, "
-	       "needwdtr = 0x%x\n", scsi_id, p->needwdtr);
-	scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
+	printk("aic7xxx_isr: Received MSG_WDTR, scsi_id %d, channel %c "
+	       "needwdtr = 0x%x\n", scsi_id, channel, p->needwdtr);
+	scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
 
-	target_mask = (0x01 << scsi_id);
 	if (p->wdtr_pending & target_mask)
 	{
 	  /*
@@ -1696,13 +2344,13 @@
 	  switch (bus_width)
 	  {
 	    case BUS_8_BIT:
-	      scratch = scratch & 0x7F;
+	      scratch &= 0x7F;
 	      break;
 
 	    case BUS_16_BIT:
-	      printk("aic7xxx_isr: target %d using 16 bit transfers\n",
-		     scsi_id);
-	      scratch = scratch | 0x80;
+	      printk("aic7xxx_isr: target %d, channel %c, using 16 bit transfers\n",
+		     scsi_id, channel);
+	      scratch |= 0x80;
 	      break;
 	  }
 	}
@@ -1715,25 +2363,27 @@
 	  switch (bus_width)
 	  {
 	    case BUS_8_BIT:
-	      scratch = scratch & 0x7F;
+	      scratch &= 0x7F;
 	      break;
 
 	    case BUS_32_BIT:
-	      /* Negotiate 16 bits. */
+	      /*
+               * Negotiate 16 bits.
+               */
 	      bus_width = BUS_16_BIT;
-	      /* Yes, we mean to fall thru here */
+	      /* Yes, we mean to fall thru here. */
 
 	    case BUS_16_BIT:
-	      printk("aic7xxx_isr: target %d using 16 bit transfers\n",
-		     scsi_id);
-	      scratch = scratch | 0x80;
+	      printk("aic7xxx_isr: target %d, channel %c, using 16 bit transfers\n",
+		     scsi_id, channel);
+	      scratch |= 0x80;
 	      break;
 	  }
 	  outb(bus_width | SEND_WDTR, HA_RETURN_1(base));
 	}
-	p->needwdtr = p->needwdtr & ~target_mask;
-	p->wdtr_pending = p->wdtr_pending & ~target_mask;
-	outb(scratch, HA_TARG_SCRATCH(base) + scsi_id);
+	p->needwdtr &= ~target_mask;
+	p->wdtr_pending &= ~target_mask;
+	outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
 	outb(scratch, SCSIRATE(base));
 	break;
       }
@@ -1747,64 +2397,52 @@
 	 * the target is refusing negotiation.
 	 */
 
-	unsigned char targ_scratch, scsi_id;
-	unsigned short mask;
+	scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
 
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	if (inb(SBLKCTL(base)) & 0x08)
-	{
-	  scsi_id = scsi_id + 8;
-	}
-
-	mask = (0x01 << scsi_id);
-
-	targ_scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
-
-	if (p->wdtr_pending & mask)
+	if (p->wdtr_pending & target_mask)
 	{
 	  /*
 	   * note 8bit xfers and clear flag
 	   */
-	  targ_scratch = targ_scratch & 0x7F;
-	  p->needwdtr = p->needwdtr & ~mask;
-	  p->wdtr_pending = p->wdtr_pending & ~mask;
-	  outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
-	  printk("aic7xxx: target %d refusing WIDE negotiation. Using "
-		 "8 bit transfers\n", scsi_id);
+	  scratch &= 0x7F;
+	  p->needwdtr &= ~target_mask;
+	  p->wdtr_pending &= ~target_mask;
+	  outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	  printk("aic7xxx: target %d, channel %c, refusing WIDE negotiation. "
+                 "Using 8 bit transfers\n", scsi_id, channel);
 	}
 	else
 	{
-	  if (p->sdtr_pending & mask)
+	  if (p->sdtr_pending & target_mask)
 	  {
 	    /*
 	     * note asynch xfers and clear flag
 	     */
-	    targ_scratch = targ_scratch & 0xF0;
-	    p->needsdtr = p->needsdtr & ~mask;
-	    p->sdtr_pending = p->sdtr_pending & ~mask;
-	    outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
-	    printk("aic7xxx: target %d refusing syncronous negotiation. Using "
-		   "asyncronous transfers\n", scsi_id);
+	    scratch &= 0xF0;
+	    p->needsdtr &= ~target_mask;
+	    p->sdtr_pending &= ~target_mask;
+	    outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	    printk("aic7xxx: target %d, channel %c, refusing syncronous negotiation. "
+                   "Using asyncronous transfers\n", scsi_id, channel);
 	  }
 	  /*
 	   * Otherwise, we ignore it.
 	   */
 	}
-	outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
-	outb(targ_scratch, SCSIRATE(base));
+	outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	outb(scratch, SCSIRATE(base));
 	break;
       }
 
       case BAD_STATUS:
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	outb(0, HA_RETURN_1(base));   /* CHECK_CONDITION may change this */
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -1812,62 +2450,51 @@
 	  aic7xxx_getscb(base, scb);
 	  aic7xxx_status(cmd) = scb->target_status;
 
-	  cmd->result = cmd->result | scb->target_status;
+	  cmd->result |= scb->target_status;
 
-	  /*
-	   * This test is just here for debugging purposes.
-	   * It will go away when the timeout problem is resolved.
-	   */
 	  switch (status_byte(scb->target_status))
 	  {
 	    case GOOD:
+              printk("aic7xxx_isr: Interrupted for status of 0???\n");
 	      break;
 
 	    case CHECK_CONDITION:
 	      if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
 	      {
+                unsigned char tcl;
+                unsigned char control;
 		void         *req_buf;
-#ifndef AIC7XXX_USE_SG
-		unsigned int  req_buflen;
-#endif
-
-		/* Update the timeout for the SCSI command. */
-/*                update_timeout(cmd, SENSE_TIMEOUT); */
 
-		/* Send a sense command to the requesting target. */
-		cmd->flags = cmd->flags | WAS_SENSE;
+                tcl = scb->target_channel_lun;
+		/*
+                 * Send a sense command to the requesting target.
+                 */
+		cmd->flags |= WAS_SENSE;
 		memcpy((void *) scb->sense_cmd, (void *) generic_sense,
 		       sizeof(generic_sense));
 
-		scb->sense_cmd[1] = cmd->lun << 5;
+		scb->sense_cmd[1] = (cmd->lun << 5);
 		scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
 
-#ifdef AIC7XXX_USE_SG
 		scb->sense_sg.address = (char *) &cmd->sense_buffer;
 		scb->sense_sg.length = sizeof(cmd->sense_buffer);
 		req_buf = &scb->sense_sg;
-#else
-		req_buf = &cmd->sense_buffer;
-		req_buflen = sizeof(cmd->sense_buffer);
-#endif
 		cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+                control = scb->control;
 		memset(scb, 0, SCB_DOWNLOAD_SIZE);
-		scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
-		    ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+                scb->control = control & SCB_DISCENB;
+		scb->target_channel_lun = tcl;
 		addr = scb->sense_cmd;
 		scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
 		memcpy(scb->SCSI_cmd_pointer, &addr,
 		       sizeof(scb->SCSI_cmd_pointer));
-#ifdef AIC7XXX_USE_SG
 		scb->SG_segment_count = 1;
 		memcpy(scb->SG_list_pointer, &req_buf,
 			sizeof(scb->SG_list_pointer));
-#else
-		scb->SG_segment_count = 0;
-		memcpy(scb->data_pointer, &req_buf,
-			sizeof(scb->data_pointer));
-		memcpy(scb->data_count, &req_buflen, 3);
-#endif
+                scb->data_count[0] = scb->sense_sg.length & 0xFF;
+                scb->data_count[1] = (scb->sense_sg.length >> 8) & 0xFF;
+                scb->data_count[2] = (scb->sense_sg.length >> 16) & 0xFF;
+		memcpy(scb->data_pointer, &(scb->sense_sg.address), 4);
 
 		outb(SCBAUTO, SCBCNT(base));
 		asm volatile("cld\n\t"
@@ -1878,34 +2505,16 @@
 			     :"si", "cx", "dx");
 		outb(0, SCBCNT(base));
 		outb(SCB_LIST_NULL, (SCBARRAY(base) + 30));
+                /*
+                 * Ensure that the target is "BUSY" so we don't get overlapping
+                 * commands if we happen to be doing tagged I/O.
+                 */
+                active = inb(HA_ACTIVE0(base)) | (inb(HA_ACTIVE1(base)) << 8);
+                active |= target_mask;
+                outb(active & 0xFF, HA_ACTIVE0(base));
+                outb((active >> 8) & 0xFF, HA_ACTIVE1(base));
 
-		/*
-		 * Add this SCB to the "waiting for selection" list.
-		 */
-		head = inb(WAITING_SCBH(base));
-		tail = inb(WAITING_SCBT(base));
-		if (head & SCB_LIST_NULL)
-		{ /* list is empty */
-		  head = scb->position;
-		  tail = SCB_LIST_NULL;
-		}
-		else
-		{
-		  if (tail & SCB_LIST_NULL)
-		  { /* list has one element */
-		    tail = scb->position;
-		    outb(head, SCBPTR(base));
-		    outb(tail, (SCBARRAY(base) + 30));
-		  }
-		  else
-		  { /* list has more than one element */
-		    outb(tail, SCBPTR(base));
-		    tail = scb->position;
-		    outb(tail, (SCBARRAY(base) + 30));
-		  }
-		}
-		outb(head, WAITING_SCBH(base));
-		outb(tail, WAITING_SCBT(base));
+                aic7xxx_add_waiting_scb(base, scb, LIST_HEAD);
 		outb(SEND_SENSE, HA_RETURN_1(base));
 	      }  /* first time sense, no errors */
 	      else
@@ -1915,7 +2524,7 @@
 		 * a normal command complete, and have the scsi driver handle
 		 * this condition.
 		 */
-		cmd->flags = cmd->flags | ASKED_FOR_SENSE;
+		cmd->flags |= ASKED_FOR_SENSE;
 	      }
 	      break;
 
@@ -1948,13 +2557,13 @@
 	break;
 
       case RESIDUAL:
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -1980,9 +2589,8 @@
 	    if (actual < cmd->underflow)
 	    {
 	      printk("aic7xxx: target %d underflow - "
-		     "wanted (at least) %u, got %u\n",
-		     cmd->target, cmd->underflow, actual);
-
+		     "wanted (at least) %u, got %u, count=%d\n",
+		     cmd->target, cmd->underflow, actual, inb(SCBARRAY(base + 18)));
 	      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
 	      aic7xxx_status(cmd) = scb->target_status;
 	    }
@@ -1991,13 +2599,13 @@
 	break;
 
       case ABORT_TAG:
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -2008,38 +2616,21 @@
 	   */
 	  printk("aic7xxx_isr: invalid tag recieved on channel %c "
 		 "target %d, lun %d -- sending ABORT_TAG\n",
-		  (cmd->channel & 0x01) ? 'B':'A',
-		  cmd->target, cmd->lun & 0x07);
-	  /*
-	   *  This is a critical section, since we don't want the
-	   *  queue routine mucking with the host data.
-	   */
-	  save_flags(flags);
-	  cli();
-
-	  /*
-	   *  Process the command after marking the scb as free
-	   *  and adding it to the free list.
-	   */
-	  scb->state = SCB_FREE;
-	  scb->cmd = NULL;
-	  scb->next = p->free_scb;      /* preserve next pointer */
-	  p->free_scb = scb;            /* add at head of list */
+		  channel, scsi_id, cmd->lun & 0x07);
 
-	  restore_flags(flags);
 	  cmd->result = (DID_RETRY_COMMAND << 16);
-	  cmd->scsi_done(cmd);
+          aic7xxx_done(p, scb);
 	}
 	break;
 
       case AWAITING_MSG:
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -2061,6 +2652,32 @@
 	}
 	break;
 
+      case IMMEDDONE:
+        scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
+        if (scb->state & SCB_DEVICE_RESET)
+        {
+          int found;
+
+          /*
+           * Go back to async/narrow transfers and renogiate.
+           */
+          aic7xxx_unbusy_target(scsi_id, channel, base);
+          p->needsdtr |= (p->needsdtr_copy & target_mask);
+          p->needwdtr |= (p->needwdtr_copy & target_mask);
+          p->sdtr_pending &= ~target_mask;
+          p->wdtr_pending &= ~target_mask;
+          scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
+          scratch &= SXFR;
+          outb(scratch, HA_TARG_SCRATCH(base));
+          found = aic7xxx_reset_device(p, (int) scsi_id, channel, SCB_LIST_NULL);
+        }
+        else
+        {
+          panic("aic7xxx_isr: Immediate complete for unknown operation.\n");
+        }
+        break;
+
       default:               /* unknown */
 	debug("aic7xxx_isr: seqint, intstat = 0x%x, scsisigi = 0x%x\n",
 	      intstat, inb(SCSISIGI(base)));
@@ -2074,8 +2691,8 @@
   {
     int status = inb(SSTAT1(base));
 
-    scbptr = inb(SCBPTR(base));
-    scb = &p->scb_array[scbptr];
+    scb_index = inb(SCBPTR(base));
+    scb = &(p->scb_array[scb_index]);
     if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
     {
       printk("aic7xxx_isr: no command for scb (scsiint)\n");
@@ -2128,13 +2745,11 @@
 	else
 	{
 	  active = inb(HA_ACTIVE0(base));
-	  active = active & ~(target_mask);
+	  active &= ~(target_mask);
 	  outb(active, HA_ACTIVE0(base));
 	}
 
-#ifdef AIC7XXX_USE_DMA
 	outb(SCB_NEEDDMA, SCBARRAY(base));
-#endif
 
 	/*
 	 * Shut off the offending interrupt sources, reset
@@ -2155,32 +2770,16 @@
 
 	outb(CLRSCSIINT, CLRINT(base));
 
-	/* Shift the waiting for selection queue forward */
+	/*
+         * Shift the waiting for selection queue forward
+         */
 	waiting = inb(WAITING_SCBH(base));
 	outb(waiting, SCBPTR(base));
 	waiting = inb(SCBARRAY(base) + 30);
 	outb(waiting, WAITING_SCBH(base));
 
 	RESTART_SEQUENCER(p);
-	/*
-	 * This is a critical section, since we don't want the
-	 * queue routine mucking with the host data.
-	 */
-	save_flags(flags);
-	cli();
-
-	/*
-	 * Process the command after marking the scb as free
-	 * and adding it to the free list.
-	 */
-	scb->state = SCB_FREE;
-	scb->cmd = NULL;
-	scb->next = p->free_scb;        /* preserve next pointer */
-	p->free_scb = scb;              /* add at head of list */
-
-	restore_flags(flags);
-
-	cmd->scsi_done(cmd);
+        aic7xxx_done(p, scb);
 #if 0
   printk("aic7xxx_isr: SELTO scb(%d) state(%x), cmd(%x)\n",
 	 scb->position, scb->state, (unsigned int) scb->cmd);
@@ -2246,9 +2845,9 @@
       {
 	printk("aic7xxx warning: "
 	       "no command for scb %d (cmdcmplt)\n"
-	       "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x\n",
+	       "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x, pos = %d\n",
 	       complete, inb(QOUTFIFO(base)),
-	       scb->state, (unsigned int) scb->cmd);
+	       scb->state, (unsigned int) scb->cmd, scb->position);
 	outb(CLRCMDINT, CLRINT(base));
 	continue;
       }
@@ -2256,26 +2855,28 @@
 
       cmd->result = (aic7xxx_error(cmd) << 16) | aic7xxx_status(cmd);
       if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
-      { /* Got sense information. */
-	cmd->flags = cmd->flags & ASKED_FOR_SENSE;
+      {
+        /*
+         * Got sense information.
+         */
+	cmd->flags &= ASKED_FOR_SENSE;
       }
 #if 0
       printk("aic7xxx_intr: (complete) state = %d, cmd = 0x%x, free = 0x%x\n",
 	     scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
 #endif
+
       /*
-       * This is a critical section, since we don't want the
-       * queue routine mucking with the host data.
+       * Clear interrupt status before checking
+       * the output queue again. This eliminates
+       * a race condition whereby a command could
+       * complete between the queue poll and the
+       * interrupt clearing, so notification of the
+       * command being complete never made it back
+       * up to the kernel.
        */
-      save_flags(flags);
-      cli();
-
-      scb->state = SCB_FREE;
-      scb->next = p->free_scb;
-      scb->cmd = NULL;
-      p->free_scb = &(p->scb_array[scb->position]);
-
-      restore_flags(flags);
+      outb(CLRCMDINT, CLRINT(base));
+      aic7xxx_done(p, scb);
 #if 0
   if (scb != &p->scb_array[scb->position])
   {
@@ -2285,18 +2886,48 @@
 	 scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
 #endif
 
-      cmd->scsi_done(cmd);
-
+#ifdef AIC7XXX_PROC_STATS
       /*
-       * Clear interrupt status before checking
-       * the output queue again. This eliminates
-       * a race condition whereby a command could
-       * complete between the queue poll and the
-       * interrupt clearing, so notification of the
-       * command being complete never made it back
-       * up to the kernel.
+       * XXX: we should actually know how much actually transferred
+       * XXX: for each command, but apparently that's too difficult.
        */
-      outb(CLRCMDINT, CLRINT(base));
+      actual = aic7xxx_length(cmd, 0);
+      if (((cmd->flags & WAS_SENSE) == 0) && (actual > 0))
+      {
+        struct aic7xxx_xferstats *sp;
+        long *ptr;
+        int x;
+
+        sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+        sp->xfers++;
+
+        if (cmd->request.cmd == WRITE)
+        {
+          sp->w_total++;
+          sp->w_total512 += (actual >> 9);
+          ptr = sp->w_bins;
+        }
+        else
+        {
+          sp->r_total++;
+          sp->r_total512 += (actual >> 9);
+          ptr = sp->r_bins;
+        }
+        for (x = 9; x <= 17; x++)
+        {
+          if (actual < (1 << x))
+          {
+            ptr[x - 9]++;
+            break;
+          }
+        }
+        if (x > 17)
+        {
+          ptr[x - 9]++;
+        }
+      }
+#endif /* AIC7XXX_PROC_STATS */
+
     } while (inb(QOUTCNT(base)));
   }
 }
@@ -2449,7 +3080,7 @@
   timeout = jiffies + 100;  /* 1 second timeout */
   while ((jiffies < timeout) && ((inb(SEECTL(base)) & SEERDY) == 0))
   {
-    ;  /* Do nothing!  Wait for access to be granted. */
+    ; /* Do nothing!  Wait for access to be granted.  */
   }
   if ((inb(SEECTL(base)) & SEERDY) == 0)
   {
@@ -2465,7 +3096,9 @@
    */
   for (k = 0; k < (sizeof(*sc) / 2); k++)
   {
-    /* Send chip select for one clock cycle. */
+    /*
+     * Send chip select for one clock cycle.
+     */
     outb(SEEMS | SEECK | SEECS, SEECTL(base));
     CLOCK_PULSE(base);
 
@@ -2482,7 +3115,9 @@
       outb(temp, SEECTL(base));
       CLOCK_PULSE(base);
     }
-    /* Send the 6 bit address (MSB first, LSB last). */
+    /*
+     * Send the 6 bit address (MSB first, LSB last).
+     */
     for (i = 5; i >= 0; i--)
     {
       temp = k;
@@ -2523,7 +3158,9 @@
       checksum = checksum + seeprom[k];
     }
 
-    /* Reset the chip select for the next command cycle. */
+    /*
+     * Reset the chip select for the next command cycle.
+     */
     outb(SEEMS, SEECTL(base));
     CLOCK_PULSE(base);
     outb(SEEMS | SEECK, SEECTL(base));
@@ -2532,11 +3169,10 @@
     CLOCK_PULSE(base);
   }
 
-  if (checksum != sc->checksum)
-  {
-    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
-    return (0);
-  }
+  /*
+   * Release access to the memory port and the serial EEPROM.
+   */
+  outb(0, SEECTL(base));
 
 #if 0
   printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
@@ -2552,8 +3188,12 @@
   printk("\n");
 #endif
 
-  /* Release access to the memory port and the serial EEPROM. */
-  outb(0, SEECTL(base));
+  if (checksum != sc->checksum)
+  {
+    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+    return (0);
+  }
+
   return (1);
 }
 
@@ -2586,7 +3226,10 @@
       sblkctl_reg = inb(SBLKCTL(base)) ^ AUTOFLUSHDIS;
       outb(sblkctl_reg, SBLKCTL(base));
       if (inb(SBLKCTL(base)) == sblkctl_reg)
-      {  /* We detected a Rev E board. */
+      {
+        /*
+         * We detected a Rev E board.
+         */
 	printk("aic7770: Rev E and subsequent; using 4 SCB's\n");
 	outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL(base));
 	maxscb = 4;
@@ -2641,6 +3284,7 @@
   unsigned char sblkctl;
   int max_targets;
   int found = 1;
+  int bios_disabled = 0;
   unsigned char target_settings;
   unsigned char scsi_conf, host_conf;
   int have_seeprom = 0;
@@ -2713,6 +3357,10 @@
        * since there was some issue about reseting the board.
        */
       config.irq = inb(HA_INTDEF(config.base)) & 0x0F;
+      if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED)
+      {
+        bios_disabled = 1;
+      }
       host_conf = inb(HA_HOSTCONF(config.base));
       config.busrtime = host_conf & 0x3C;
       /* XXX Is this valid for motherboard based controllers? */
@@ -2736,6 +3384,10 @@
       config.pause = REQ_PAUSE; /* DWG would like to be like the rest */
       config.extended = aic7xxx_extended;
       config.irq = inb(HA_INTDEF(config.base)) & 0x0F;
+      if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED)
+      {
+        bios_disabled = 1;
+      }
       host_conf = inb(HA_HOSTCONF(config.base));
       config.busrtime = host_conf & 0x3C;
       /* XXX Is this valid for motherboard based controllers? */
@@ -2772,7 +3424,7 @@
       else
       {
 	printk("done\n");
-	config.extended = (sc.bios_control & CFEXTEND) >> 7;
+	config.extended = ((sc.bios_control & CFEXTEND) >> 7);
 	config.scsi_id = (sc.brtime_id & CFSCSIID);
 	config.parity = (sc.adapter_control & CFSPARITY) ?
 			 AIC_ENABLED : AIC_DISABLED;
@@ -2780,7 +3432,7 @@
 			      AIC_ENABLED : AIC_DISABLED;
 	config.high_term = (sc.adapter_control & CFWSTERM) ?
 			      AIC_ENABLED : AIC_DISABLED;
-	config.busrtime = (sc.brtime_id & CFBRTIME) >> 8;
+	config.busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
       }
 
       /*
@@ -2827,13 +3479,13 @@
   sblkctl = inb(SBLKCTL(base)) & 0x0F;  /* mask out upper two bits */
   switch (sblkctl)
   {
-    case 0:     /* narrow/normal bus */
+    case SELSINGLE:     /* narrow/normal bus */
       config.scsi_id = inb(HA_SCSICONF(base)) & 0x07;
       config.bus_type = AIC_SINGLE;
-      outb(0, HA_FLAGS(base));
+      outb(SINGLE_BUS, HA_FLAGS(base));
       break;
 
-    case 2:     /* Wide bus */
+    case SELWIDE:     /* Wide bus */
       config.scsi_id = inb(HA_SCSICONF(base) + 1) & 0x0F;
       config.bus_type = AIC_WIDE;
       printk("aic7xxx: Enabling wide channel of %s-Wide\n",
@@ -2841,7 +3493,7 @@
       outb(WIDE_BUS, HA_FLAGS(base));
       break;
 
-    case 8:     /* Twin bus */
+    case SELBUSB:     /* Twin bus */
       config.scsi_id = inb(HA_SCSICONF(base)) & 0x07;
 #ifdef AIC7XXX_TWIN_SUPPORT
       config.scsi_id_b = inb(HA_SCSICONF(base) + 1) & 0x07;
@@ -2940,11 +3592,7 @@
    */
   host = scsi_register(template, sizeof(struct aic7xxx_host));
   host->can_queue = config.maxscb;
-#ifdef AIC7XXX_TAGGED_QUEUEING
-  host->cmd_per_lun = 2;
-#else
-  host->cmd_per_lun = 1;
-#endif
+  host->cmd_per_lun = AIC7XXX_CMDS_PER_LUN;
   host->this_id = config.scsi_id;
   host->irq = config.irq;
   if (config.bus_type == AIC_WIDE)
@@ -2958,7 +3606,9 @@
 
   p = (struct aic7xxx_host *) host->hostdata;
 
-  /* Initialize the scb array by setting the state to free. */
+  /*
+   * Initialize the scb array by setting the state to free.
+   */
   for (i = 0; i < AIC7XXX_MAXSCB; i++)
   {
     p->scb_array[i].state = SCB_FREE;
@@ -3025,10 +3675,12 @@
   printk("aic7xxx: Downloading sequencer code..");
   aic7xxx_loadseq(base);
 
-  /* Set Fast Mode and Enable the board */
+  /*
+   * Set Fast Mode and Enable the board
+   */
   outb(FASTMODE, SEQCTL(base));
 
-  if ((p->type == AIC_274x || p->type == AIC_284x))
+  if ((p->type == AIC_274x) || (p->type == AIC_284x))
   {
     outb(ENABLE, BCTL(base));
   }
@@ -3046,18 +3698,20 @@
      */
     outb(config.scsi_id_b, SCSIID(base));
     scsi_conf = inb(HA_SCSICONF(base) + 1) & (ENSPCHK | STIMESEL);
-    scsi_conf = scsi_conf | ENSTIMER | ACTNEGEN | STPWEN;
-    outb(scsi_conf, SXFRCTL1(base));
+    outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base));
     outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
-    /* Select Channel A */
-    outb(0, SBLKCTL(base));
+    /*
+     * Select Channel A
+     */
+    outb(SELSINGLE, SBLKCTL(base));
   }
   outb(config.scsi_id, SCSIID(base));
   scsi_conf = inb(HA_SCSICONF(base)) & (ENSPCHK | STIMESEL);
   outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base));
   outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
 
-  /* Look at the information that board initialization or the board
+  /*
+   * Look at the information that board initialization or the board
    * BIOS has left us. In the lower four bits of each target's
    * scratch space any value other than 0 indicates that we should
    * initiate synchronous transfers. If it's zero, the user or the
@@ -3076,19 +3730,43 @@
   {
     max_targets = 16;
   }
+  /*
+   * Grab the disconnection disable table and invert it for our needs
+   */
+  if (have_seeprom)
+  {
+    p->discenable = 0;
+  }
+  else
+  {
+    if (bios_disabled)
+    {
+      printk("aic7xxx : Host Adapter Bios disabled.  Using default SCSI "
+             "device parameters\n");
+      p->discenable = 0xFFFF;
+    }
+    else
+    {
+      p->discenable = ~(inw(HA_DISC_DSB(base)));
+    }
+  }
 
   for (i = 0; i < max_targets; i++)
   {
     if (have_seeprom)
     {
-      target_settings = (sc.device_flags[i] & CFXFER) << 4;
+      target_settings = ((sc.device_flags[i] & CFXFER) << 4);
       if (sc.device_flags[i] & CFSYNCH)
       {
-	p->needsdtr_copy = p->needsdtr_copy | (0x01 << i);
+	p->needsdtr_copy |= (0x01 << i);
       }
       if ((sc.device_flags[i] & CFWIDEB) && (p->bus_type == AIC_WIDE))
       {
-	p->needwdtr_copy = p->needwdtr_copy | (0x01 << i);
+	p->needwdtr_copy |= (0x01 << i);
+      }
+      if (sc.device_flags[i] & CFDISC)
+      {
+        p->discenable |= (0x01 << i);
       }
     }
     else
@@ -3096,11 +3774,11 @@
       target_settings = inb(HA_TARG_SCRATCH(base) + i);
       if (target_settings & 0x0F)
       {
-	p->needsdtr_copy = p->needsdtr_copy | (0x01 << i);
+	p->needsdtr_copy |= (0x01 << i);
 	/*
 	 * Default to asynchronous transfers (0 offset)
 	 */
-	target_settings = target_settings & 0xF0;
+	target_settings &= 0xF0;
       }
       /*
        * If we are not wide, forget WDTR. This makes the driver
@@ -3109,8 +3787,8 @@
        */
       if ((target_settings & 0x80) && (p->bus_type == AIC_WIDE))
       {
-	p->needwdtr_copy = p->needwdtr_copy | (0x01 << i);
-	target_settings = target_settings & 0x7F;
+	p->needwdtr_copy |= (0x01 << i);
+	target_settings &= 0x7F;
       }
     }
     outb(target_settings, (HA_TARG_SCRATCH(base) + i));
@@ -3121,7 +3799,7 @@
 #if 0
   printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
   printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
-#endif 0
+#endif
 
   /*
    * Clear the control byte for every SCB so that the sequencer
@@ -3145,9 +3823,11 @@
   outb(0, HA_ACTIVE0(base));
   outb(0, HA_ACTIVE1(base));
 
-  /* We don't have any waiting selections */
-  outb (SCB_LIST_NULL, WAITING_SCBH(base));
-  outb (SCB_LIST_NULL, WAITING_SCBT(base));
+  /*
+   * We don't have any waiting selections
+   */
+  outb(SCB_LIST_NULL, WAITING_SCBH(base));
+  outb(SCB_LIST_NULL, WAITING_SCBT(base));
 
   /*
    * Reset the SCSI bus. Is this necessary?
@@ -3170,14 +3850,14 @@
       /*
        * Select channel B.
        */
-      outb(2, SBLKCTL(base));
+      outb(SELBUSB, SBLKCTL(base));
       outb(SCSIRSTO, SCSISEQ(base));
       udelay(1000);
       outb(0, SCSISEQ(base));
       /*
        * Select channel A.
        */
-      outb(0, SBLKCTL(base));
+      outb(SELSINGLE, SBLKCTL(base));
     }
 
     outb(SCSIRSTO, SCSISEQ(base));
@@ -3390,7 +4070,7 @@
 	 */
 	aic7xxx_spurious_count = 0;
 
-	index += 1;
+	index++;
       }
     }
   }
@@ -3414,8 +4094,8 @@
 		 struct aic7xxx_scb *scb)
 {
   void *addr;
-  unsigned length;
   unsigned short mask;
+  struct scatterlist *sg;
 
   /*
    * Setup the control byte if we need negotiation and have not
@@ -3432,15 +4112,19 @@
       cmd->device->current_tag = 1;  /* enable tagging */
     }
     cmd->tag = cmd->device->current_tag;
-    cmd->device->current_tag = cmd->device->current_tag + 1;
-    scb->control = scb->control | SCB_TE;
+    cmd->device->current_tag++;
+    scb->control |= SCB_TE;
   }
 #endif
-  mask = (0x01 << cmd->target);
+  mask = (0x01 << (cmd->target | (cmd->channel << 3)));
+  if (p->discenable & mask)
+  {
+    scb->control |= SCB_DISCENB;
+  }
   if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
   {
-    p->wdtr_pending = p->wdtr_pending | mask;
-    scb->control = scb->control | SCB_NEEDWDTR;
+    p->wdtr_pending |= mask;
+    scb->control |= SCB_NEEDWDTR;
 #if 0
     printk("Sending WDTR request to target %d.\n", cmd->target);
 #endif
@@ -3449,8 +4133,8 @@
   {
     if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
     {
-      p->sdtr_pending = p->sdtr_pending | mask;
-      scb->control = scb->control | SCB_NEEDSDTR;
+      p->sdtr_pending |= mask;
+      scb->control |= SCB_NEEDSDTR;
 #if 0
       printk("Sending SDTR request to target %d.\n", cmd->target);
 #endif
@@ -3469,17 +4153,7 @@
    * changes depending on whether or not use_sg is zero; a
    * non-zero use_sg indicates the number of elements in the
    * scatter-gather array.
-   *
-   * The AIC-7770 can't support transfers of any sort larger
-   * than 2^24 (three-byte count) without backflips. For what
-   * the kernel is doing, this shouldn't occur. I hope.
    */
-  length = aic7xxx_length(cmd, 0);
-
-  if (length > 0xFFFFFF)
-  {
-    panic("aic7xxx_buildscb: can't transfer > 2^24 - 1 bytes\n");
-  }
 
   /*
    * XXX - this relies on the host data being stored in a
@@ -3493,29 +4167,47 @@
   {
 #if 0
     debug("aic7xxx_buildscb: SG used, %d segments, length %u\n",
-	  cmd->use_sg, length);
+           cmd->use_sg, length);
 #endif
     scb->SG_segment_count = cmd->use_sg;
     memcpy(scb->SG_list_pointer, &cmd->request_buffer,
 	   sizeof(scb->SG_list_pointer));
+    memcpy(&sg, &cmd->request_buffer, sizeof(sg));
+    memcpy(scb->data_pointer, &(sg[0].address), sizeof(scb->data_pointer));
+    scb->data_count[0] = sg[0].length & 0xFF;
+    scb->data_count[1] = (sg[0].length >> 8) & 0xFF;
+    scb->data_count[2] = (sg[0].length >> 16) & 0xFF;
   }
   else
   {
 #if 0
-    debug("aic7xxx_buildscb: Creating scatterlist, addr=0x%lx, length=%d.\n",
-	  (unsigned long) cmd->request_buffer, cmd->request_bufflen);
-#endif
-#ifdef AIC7XXX_USE_SG
-    scb->SG_segment_count = 1;
-    scb->sg.address = (char *) cmd->request_buffer;
-    scb->sg.length = cmd->request_bufflen;
-    addr = &scb->sg;
-    memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-#else
-    scb->SG_segment_count = 0;
-    memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer));
-    memcpy(scb->data_count, &cmd->request_bufflen, 3);
+  debug("aic7xxx_buildscb: Creating scatterlist, addr=0x%lx, length=%d.\n",
+	(unsigned long) cmd->request_buffer, cmd->request_bufflen);
 #endif
+    if (cmd->request_bufflen == 0)
+    {
+      /*
+       * In case the higher level SCSI code ever tries to send a zero
+       * length command, ensure the SCB indicates no data.  The driver
+       * will interpret a zero length command as a Bus Device Reset.
+       */
+      scb->SG_segment_count = 0;
+      memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
+      memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
+      memset(scb->data_count, 0, sizeof(scb->data_count));
+    }
+    else
+    {
+      scb->SG_segment_count = 1;
+      scb->sg.address = (char *) cmd->request_buffer;
+      scb->sg.length = cmd->request_bufflen;
+      addr = &scb->sg;
+      memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
+      scb->data_count[0] = scb->sg.length & 0xFF;
+      scb->data_count[1] = (scb->sg.length >> 8) & 0xFF;
+      scb->data_count[2] = (scb->sg.length >> 16) & 0xFF;
+      memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer));
+    }
   }
 }
 
@@ -3530,17 +4222,16 @@
 aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
 {
   long flags;
-#ifndef AIC7XXX_USE_DMA
-  int old_scbptr;
-#endif
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
   unsigned char curscb;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
 
-  /* Check to see if channel was scanned. */
- if (!p->a_scanned && (cmd->channel == 0))
+  /*
+   * Check to see if channel was scanned.
+   */
+  if (!p->a_scanned && (cmd->channel == 0))
   {
     printk("aic7xxx: Scanning channel A for devices.\n");
     p->a_scanned = 1;
@@ -3615,13 +4306,11 @@
       scb->state = SCB_ACTIVE;
       scb->next_waiting = SCB_LIST_NULL;
       memcpy(scb->host_scb, &scb, sizeof(scb));
-#ifdef AIC7XXX_USE_DMA
       scb->control = SCB_NEEDDMA;
-#endif
       PAUSE_SEQUENCER(p);
       curscb = inb(SCBPTR(p->base));
       outb(scb->position, SCBPTR(p->base));
-      aic7xxx_putdmascb(p->base, scb);
+      aic7xxx_putscb_dma(p->base, scb);
       outb(curscb, SCBPTR(p->base));
       UNPAUSE_SEQUENCER(p);
       scb->control = 0;
@@ -3630,6 +4319,9 @@
 
   scb->cmd = cmd;
   aic7xxx_position(cmd) = scb->position;
+#if 0
+  debug_scb(scb);
+#endif;
 
   /*
    * Construct the SCB beforehand, so the sequencer is
@@ -3660,17 +4352,8 @@
    * the SCB, then write its pointer into the queue in FIFO
    * and restore the saved SCB pointer.
    */
-#ifdef AIC7XXX_USE_DMA
-  aic7xxx_putscb(p->base, scb);
-#else
-  old_scbptr = inb(SCBPTR(p->base));
-  outb(scb->position, SCBPTR(p->base));
-
   aic7xxx_putscb(p->base, scb);
 
-  outb(scb->position, QINFIFO(p->base));
-  outb(old_scbptr, SCBPTR(p->base));
-#endif
   /*
    * Make sure the Scsi_Cmnd pointer is saved, the struct it
    * points to is set up properly, and the parity error flag
@@ -3680,205 +4363,149 @@
   cmd->scsi_done = fn;
   aic7xxx_error(cmd) = DID_OK;
   aic7xxx_status(cmd) = 0;
-
+  scb->timer_status = 0x0;
   cmd->result = 0;
   memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
 
   UNPAUSE_SEQUENCER(p);
+#if 0
+  printk("aic7xxx_queue: After - cmd = 0x%lx, scb->cmd = 0x%lx, pos = %d\n",
+         (long) cmd, (long) scb->cmd, scb->position);
+#endif;
   restore_flags(flags);
   return (0);
 }
 
-/* return values from aic7xxx_kill */
-typedef enum {
-  k_ok,             /* scb found and message sent */
-  k_busy,           /* message already present */
-  k_absent,         /* couldn't locate scb */
-  k_disconnect,     /* scb found, but disconnected */
-} k_state;
-
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_kill
+ *   aic7xxx_abort_scb
  *
  * Description:
- *   This must be called with interrupts disabled - it's going to
- *   be messing around with the host data, and an interrupt being
- *   fielded in the middle could get ugly.
- *
- *   Since so much of the abort and reset code is shared, this
- *   function performs more magic than it really should. If the
- *   command completes ok, then it will call scsi_done with the
- *   result code passed in. The unpause parameter controls whether
- *   or not the sequencer gets unpaused - the reset function, for
- *   instance, may want to do something more aggressive.
- *
- *   Note that the command is checked for in our SCB_array first
- *   before the sequencer is paused, so if k_absent is returned,
- *   then the sequencer is NOT paused.
+ *   Abort an scb.  If the scb has not previously been aborted, then
+ *   we attempt to send a BUS_DEVICE_RESET message to the target.  If
+ *   the scb has previously been unsuccessfully aborted, then we will
+ *   reset the channel and have all devices renegotiate.
  *-F*************************************************************************/
-static k_state
-aic7xxx_kill(Scsi_Cmnd *cmd, unsigned char message,
-	     unsigned int result, int unpause)
+static void
+aic7xxx_abort_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_host *p;
-  struct aic7xxx_scb *scb;
-  int i, active_scb, found, queued;
-  unsigned char scbsave[AIC7XXX_MAXSCB];
-  unsigned char flags;
-  int scb_control;
-  k_state status;
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = &p->scb_array[aic7xxx_position(cmd)];
-
-#if 0
-  printk("aic7xxx_kill: In the kill function...\n");
-#endif
-  PAUSE_SEQUENCER(p);
+  int base = p->base;
+  int found = 0;
+  char channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
 
   /*
-   * Case 1: In the QINFIFO
-   *
-   * This is the best case, really. Check to see if the
-   * command is still in the sequencer's input queue. If
-   * so, simply remove it. Reload the queue afterward.
+   * Ensure that the card doesn't do anything
+   * behind our back.
    */
-  queued = inb(QINCNT(p->base));
-
-  for (i = found = 0; i < (queued - found); i++)
-  {
-    scbsave[i] = inb(QINFIFO(p->base));
-
-    if (scbsave[i] == scb->position)
-    {
-      found = 1;
-      i--;
-    }
-  }
-
-  for (queued = 0; queued < i; queued++)
-  {
-    outb(scbsave[queued], QINFIFO(p->base));
-  }
-
-  if (found)
-  {
-    status = k_ok;
-    goto complete;
-  }
+  PAUSE_SEQUENCER(p);
 
-  active_scb = inb(SCBPTR(p->base));
   /*
-   * Case 2: Not the active command
-   *
-   * Check the current SCB bank. If it's not the one belonging
-   * to the command we want to kill, select the scb we want to
-   * abort and turn off the disconnected bit. The driver will
-   * then abort the command and notify us of the abort.
+   * First, determine if we want to do a bus reset or simply a bus device
+   * reset.  If this is the first time that a transaction has timed out,
+   * just schedule a bus device reset.  Otherwise, we reset the bus and
+   * abort all pending I/Os on that bus.
    */
-  if (active_scb != scb->position)
+  if (scb->state & SCB_ABORTED)
   {
-    outb(scb->position, SCBPTR(p->base));
-    scb_control = inb(SCBARRAY(p->base));
-    scb_control = scb_control & ~SCB_DIS;
-    outb(scb_control, SCBARRAY(p->base));
-    outb(active_scb, SCBPTR(p->base));
-    status = k_disconnect;
-    goto complete;
+    /*
+     * Been down this road before.  Do a full bus reset.
+     */
+    found = aic7xxx_reset_channel(p, channel, scb->position);
   }
-
-  scb_control = inb(SCBARRAY(p->base));
-  if (scb_control & SCB_DIS)
+  else
   {
-    scb_control = scb_control & ~SCB_DIS;
-    outb(scb_control, SCBARRAY(p->base));
-    status = k_disconnect;
-    goto complete;
-  }
+    unsigned char active_scb, control;
+    struct aic7xxx_scb *active_scbp;
 
-  /*
-   * Presumably at this point our target command is active. Check
-   * to see if there's a message already in effect. If not, place
-   * our message in and assert ATN so the target goes into MESSAGE
-   * OUT phase.
-   */
-  flags = inb(HA_FLAGS(p->base));
-  if (flags & ACTIVE_MSG)
-  {
     /*
-     * If there is a message in progress, reset the bus
-     * and have all devices renegotiate.
+     * Send a Bus Device Reset Message:
+     * The target we select to send the message to may be entirely
+     * different than the target pointed to by the scb that timed
+     * out.  If the command is in the QINFIFO or the waiting for
+     * selection list, its not tying up the bus and isn't responsible
+     * for the delay so we pick off the active command which should
+     * be the SCB selected by SCBPTR.  If its disconnected or active,
+     * we device reset the target scbp points to.  Although it may
+     * be that this target is not responsible for the delay, it may
+     * may also be that we're timing out on a command that just takes
+     * too much time, so we try the bus device reset there first.
      */
-    if (cmd->channel & 0x01)
+    active_scb = inb(SCBPTR(base));
+    active_scbp = &(p->scb_array[active_scb]);
+    control = inb(SCBARRAY(base));
+
+    /*
+     * Test to see if scbp is disconnected
+     */
+    outb(scb->position, SCBPTR(base));
+    if (inb(SCBARRAY(base)) & SCB_DIS)
     {
-      p->needsdtr = p->needsdtr_copy & 0xFF00;
-      p->sdtr_pending = p->sdtr_pending & 0x00FF;
-      outb(0, HA_ACTIVE1(p->base));
+      scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
+      scb->SG_segment_count = 0;
+      memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
+      memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
+      memset(scb->data_count, 0, sizeof(scb->data_count));
+      outb(SCBAUTO, SCBCNT(base));
+      asm volatile("cld\n\t"
+                   "rep\n\t"
+                   "outsb"
+                   : /* no output */
+                   :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
+                   :"si", "cx", "dx");
+      outb(0, SCBCNT(base));
+      aic7xxx_add_waiting_scb(base, scb, LIST_SECOND);
+      aic7xxx_scb_tsleep(p, scb, 2 * HZ);  /* unpauses the sequencer */
     }
     else
     {
-      if (p->bus_type == AIC_WIDE)
+      /*
+       * Is the active SCB really active?
+       */
+      if ((active_scbp->state & SCB_ACTIVE) && (control & SCB_NEEDDMA))
       {
-	p->needsdtr = p->needsdtr_copy;
-	p->needwdtr = p->needwdtr_copy;
-	p->sdtr_pending = 0;
-	p->wdtr_pending = 0;
-	outb(0, HA_ACTIVE0(p->base));
-	outb(0, HA_ACTIVE1(p->base));
+        unsigned char flags = inb(HA_FLAGS(base));
+        if (flags & ACTIVE_MSG)
+        {
+          /*
+           * If we're in a message phase, tacking on another message
+           * may confuse the target totally. The bus is probably wedged,
+           * so reset the channel.
+           */
+          channel = (active_scbp->target_channel_lun & SELBUSB) ? 'B': 'A';
+          aic7xxx_reset_channel(p, channel, scb->position);
+        }
+        else
+        {
+          /*
+           * Load the message buffer and assert attention.
+           */
+          active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
+          outb(flags | ACTIVE_MSG, HA_FLAGS(base));
+          outb(1, HA_MSG_LEN(base));
+          outb(MSG_BUS_DEVICE_RESET, HA_MSG_START(base));
+          if (active_scbp->target_channel_lun != scb->target_channel_lun)
+          {
+            /*
+             * XXX - We would like to increment the timeout on scb, but
+             *       access to that routine is denied because it is hidden
+             *       in scsi.c.  If we were able to do this, it would give
+             *       scb a new lease on life.
+             */
+            ;
+          }
+          aic7xxx_scb_tsleep(p, active_scbp, 2 * HZ);
+        }
       }
       else
       {
-	p->needsdtr = p->needsdtr_copy & 0x00FF;
-	p->sdtr_pending = p->sdtr_pending & 0xFF00;
-	outb(0, HA_ACTIVE0(p->base));
+        /*
+         * No active command to single out, so reset
+         * the bus for the timed out target.
+         */
+        aic7xxx_reset_channel(p, channel, scb->position);
       }
     }
-    /* Reset the bus. */
-    outb(SCSIRSTO, SCSISEQ(p->base));
-    udelay(1000);
-    outb(0, SCSISEQ(p->base));
-    aic7xxx_delay(AIC7XXX_RESET_DELAY);
-
-    status = k_busy;
-    goto complete;
-  }
-
-  outb(flags | ACTIVE_MSG, HA_FLAGS(p->base));    /* active message */
-  outb(1, HA_MSG_LEN(p->base));                   /* length = 1 */
-  outb(message, HA_MSG_START(p->base));           /* message body */
-
-  /*
-   * Assert ATN. Use the value of SCSISIGO saved by the
-   * sequencer code so we don't alter its contents radically
-   * in the middle of something critical.
-   */
-  outb(inb(HA_SIGSTATE(p->base)) | 0x10, SCSISIGO(p->base));
-
-  status = k_ok;
-
-  /*
-   * The command has been killed. Do the bookkeeping, unpause
-   * the sequencer, and notify the higher-level SCSI code.
-   */
-complete:
-  if (unpause)
-  {
-    UNPAUSE_SEQUENCER(p);
   }
-
-  /*
-   * Mark the scb as free and clear the scbs command pointer.
-   * Add the scb to the head of the free list being careful
-   * to preserve the next pointers.
-   */
-  scb->state = SCB_FREE;          /* mark the scb as free */
-  scb->cmd = NULL;                /* clear the command pointer */
-  scb->next = p->free_scb;        /* preserve next pointer */
-  p->free_scb = scb;              /* add at head of free list */
-  cmd->result = cmd->result << 16;
-  cmd->scsi_done(cmd);
-  return (status);
 }
 
 /*+F*************************************************************************
@@ -3891,23 +4518,20 @@
 int
 aic7xxx_abort(Scsi_Cmnd *cmd)
 {
-  int rv;
+  struct aic7xxx_scb  *scb;
+  struct aic7xxx_host *p;
   long flags;
 
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  scb = &(p->scb_array[aic7xxx_position(cmd)]);
+
   save_flags(flags);
   cli();
 
-  switch (aic7xxx_kill(cmd, ABORT, DID_ABORT, !0))
-  {
-    case k_ok:          rv = SCSI_ABORT_SUCCESS;        break;
-    case k_busy:        rv = SCSI_ABORT_BUSY;           break;
-    case k_absent:      rv = SCSI_ABORT_NOT_RUNNING;    break;
-    case k_disconnect:  rv = SCSI_ABORT_SNOOZE;         break;
-    default:            panic("aic7xxx_abort: internal error\n");
-  }
+  aic7xxx_abort_scb(p, scb);
 
   restore_flags(flags);
-  return (rv);
+  return (0);
 }
 
 /*+F*************************************************************************
@@ -3923,76 +4547,7 @@
 int
 aic7xxx_reset(Scsi_Cmnd *cmd)
 {
-  long flags;
-  struct aic7xxx_host *p;
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  save_flags(flags);
-  cli();
-
-  switch (aic7xxx_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0))
-  {
-    case k_ok:
-      /*
-       * The RESET message was sent to the target
-       * with no problems. Flag that target as
-       * needing a SDTR negotiation on the next
-       * connection and restart the sequencer.
-       */
-      p->needsdtr = p->needsdtr & (1 << cmd->target);
-      UNPAUSE_SEQUENCER(p);
-      break;
-
-    case k_absent:
-      /*
-       * The sequencer will not be paused if aic7xxx_kill()
-       * couldn't find the command.
-       */
-      PAUSE_SEQUENCER(p);
-      /* falls through */
-
-    case k_busy:
-      cmd->result = DID_RESET << 16;  /* return reset code */
-      cmd->scsi_done(cmd);
-      break;
-
-    case k_disconnect:
-      /*
-       * Do a hard reset of the SCSI bus. According to the
-       * SCSI-2 draft specification, reset has to be asserted
-       * for at least 25us. I'm invoking the kernel delay
-       * function for 30us since I'm not totally trusting of
-       * the busy loop timing.
-       *
-       * XXX - I'm not convinced this works. I tried resetting
-       *       the bus before, trying to get the devices on the
-       *       bus to revert to asynchronous transfer, and it
-       *       never seemed to work.
-       */
-      debug("aic7xxx: attempting to reset scsi bus and card\n");
-
-      outb(SCSIRSTO, SCSISEQ(p->base));
-      udelay(1000);
-      outb(0, SCSISEQ(p->base));
-      aic7xxx_delay(AIC7XXX_RESET_DELAY);
-
-      UNPAUSE_SEQUENCER(p);
-
-      /*
-       * Locate the command and return a "reset" status
-       * for it. This is not completely correct and will
-       * probably return to haunt me later.
-       */
-      cmd->result = DID_RESET << 16;  /* return reset code */
-      cmd->scsi_done(cmd);
-      break;
-
-    default:
-      panic("aic7xxx_reset: internal error\n");
-  }
-
-  restore_flags(flags);
-  return (SCSI_RESET_SUCCESS);
+  return (aic7xxx_abort(cmd));
 }
 
 /*+F*************************************************************************
@@ -4032,6 +4587,8 @@
 
   return (0);
 }
+
+#include "aic7xxx_proc.c"
 
 #ifdef MODULE
 /* Eventually this will go into an include file, but this will be later */
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h
--- v1.3.35/linux/drivers/scsi/aic7xxx.h	Mon Sep 18 14:53:56 1995
+++ linux/drivers/scsi/aic7xxx.h	Mon Oct 23 13:21:53 1995
@@ -18,12 +18,12 @@
  * along with this program; see the file COPYING.  If not, write to
  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  * 
- * $Id: aic7xxx.h,v 2.0 1995/08/02 05:28:42 deang Exp $
+ * $Id: aic7xxx.h,v 2.2 1995/09/20 05:18:18 deang Exp $
  *-M*************************************************************************/
 #ifndef _aic7xxx_h
 #define _aic7xxx_h
 
-#define AIC7XXX_H_VERSION  "$Revision: 2.0 $"
+#define AIC7XXX_H_VERSION  "$Revision: 2.2 $"
 
 /*
  * Scsi_Host_Template (see hosts.h) for AIC-7770/AIC-7870 - some fields
@@ -33,7 +33,7 @@
 	NULL,							\
 	NULL,							\
 	NULL,							\
-	NULL,							\
+	aic7xxx_proc_info,					\
 	NULL,							\
 	aic7xxx_detect,						\
 	NULL,							\
@@ -61,5 +61,7 @@
 extern int aic7xxx_reset(Scsi_Cmnd *);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
+
+extern int aic7xxx_proc_info(char *, char **, off_t, int, int, int);
 
 #endif /* _aic7xxx_h */
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq
--- v1.3.35/linux/drivers/scsi/aic7xxx.seq	Wed Aug  2 13:21:07 1995
+++ linux/drivers/scsi/aic7xxx.seq	Mon Oct 23 13:21:53 1995
@@ -25,9 +25,9 @@
 # optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
 ##-M#########################################################################
 
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.0 1995/08/02 05:28:42 deang Exp $"
+VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.1 1995/08/30 07:47:07 deang Exp $"
 
-SCBMASK		= 0x1f
+SCBMASK		= 0xff
 
 SCSISEQ		= 0x00
 ENRSELI		= 0x10
@@ -48,6 +48,7 @@
 SELDI		= 0x20
 CLRSINT1	= 0x0c
 SSTAT1		= 0x0c
+PHASEMIS	= 0x10
 SIMODE1		= 0x11
 SCSIBUSL	= 0x12
 SHADDR		= 0x14
@@ -83,18 +84,19 @@
 SCSICONF_B	= 0x5b
 
 #  The two reserved bytes at SCBARRAY+1[23] are expected to be set to
-#  zero, and the reserved bit in SCBARRAY+0 is used as an internal flag
-#  to indicate whether or not to reload scatter-gather parameters after
-#  a disconnect.  We also use bits 6 & 7 to indicate whether or not to
-#  initiate SDTR or WDTR repectively when starting this command.
+#  zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate 
+#  whether or not to DMA an SCB from host ram. This flag prevents the
+#  "re-fetching" of transactions that are requed because the target is
+#  busy with another command. We also use bits 6 & 7 to indicate whether 
+#  or not to initiate SDTR or WDTR repectively when starting this command.
 #
 SCBARRAY+0	= 0xa0
 
 DISCONNECTED	= 0x04
 NEEDDMA		= 0x08
-SG_LOAD		= 0x10
+NEEDSDTR	= 0x10
 TAG_ENB		= 0x20
-NEEDSDTR	= 0x40
+DISCENB		= 0x40
 NEEDWDTR	= 0x80
 
 SCBARRAY+1	= 0xa1
@@ -144,6 +146,7 @@
 						# (command was null), so tell
 						# it that it can fill the
 						# message buffer.
+IMMEDDONE	= 0xb1
 
 
 #  The host adapter card (at least the BIOS) uses 20-2f for SCSI
@@ -153,11 +156,11 @@
 #  scratchspace (actually a value that can be copied directly into
 #  SCSIRATE).  The kernel driver will enable synchronous negotiation
 #  for all targets that have a value other than 0 in the lower four
-#  bits of the target scratch space.  This should work irregardless of
-#  whether the bios has been installed. NEEDWDTR and NEEDSDTR are the top
-#  two bits of the SCB control byte.  The kernel driver will set these
-#  when a WDTR or SDTR message should be sent to the target the SCB's 
-#  command references.
+#  bits of the target scratch space.  This should work regardless of
+#  whether the bios has been installed. NEEDSDTR and NEEDWDTR are the
+#  fouth and sevent bits of the SCB control byte.  The kernel driver 
+#  will set these when a WDTR or SDTR message should be sent to the 
+#  target the SCB's command references.
 #
 #  REJBYTE contains the first byte of a MESSAGE IN message, so the driver 
 #  can report an intelligible error if a message is rejected.
@@ -168,9 +171,9 @@
 #  no idea what the lun is, and we can't select the right SCB register
 #  bank, so force a kernel panic if the target attempts a data in/out or
 #  command phase instead of corrupting something.  FLAGS also contains
-#  configuration bits so that we can optimize for TWIN and WIDE controllers
-#  as well as the MAX_OFFSET bit which we set when we want to negotiate for
-#  maximum sync offset irregardless of what the per target scratch space says.
+#  configuration bits so that we can optimize for TWIN and WIDE controllers,
+#  the MAX_OFFSET bit which we set when we want to negotiate for maximum sync 
+#  offset irregardless of what the per target scratch space says.
 #
 #  Note that SG_NEXT occupies four bytes.
 #
@@ -198,13 +201,9 @@
 # Linux users should use 0xc (12) for SG_SIZEOF
 #SG_SIZEOF	= 0x8 				# sizeof(struct ahc_dma)
 SG_SIZEOF	= 0xc 				# sizeof(struct scatterlist)
-# if AIC7XXX_USE_SG
-SCB_SIZEOF	= 0x13				# sizeof SCB to DMA (19 bytes)
-# else
-#SCB_SIZEOF	= 0x1a				# sizeof SCB without SG
-# endif
+SCB_SIZEOF	= 0x1a				# sizeof SCB to DMA (26 bytes)
 
-SG_NOLOAD	= 0x4c				# load SG pointer/length?
+DMAPARAMS	= 0x4c				# Parameters for DMA
 SG_COUNT	= 0x4d				# working value of SG count
 SG_NEXT		= 0x4e				# working value of SG pointer
 SG_NEXT+0	= 0x4e
@@ -216,6 +215,7 @@
 FLAGS		= 0x53				# Device configuration flags
 TWIN_BUS	= 0x01
 WIDE_BUS	= 0x02
+DPHASE		= 0x04
 MAX_OFFSET	= 0x08
 ACTIVE_MSG	= 0x20
 IDENTIFY_SEEN	= 0x40
@@ -240,8 +240,8 @@
 # ram since a reconnecting target can request sense and this will create
 # yet another SCB waiting for selection.  The solution used here is to 
 # use byte 31 of the SCB as a psuedo-next pointer and to thread a list
-# of SCBs that are awaiting selection.  Since 0 is a valid SCB offset, 
-# SCB_LIST_NULL is 0x10 which is out of range.  The kernel driver must
+# of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB offsets, 
+# SCB_LIST_NULL is 0xff which is out of range.  The kernel driver must
 # add an entry to this list everytime a request sense occurs.  The sequencer
 # will automatically consume the entries.
 
@@ -249,26 +249,22 @@
 						# selection
 WAITING_SCBT	= 0x58				# tail of list of SCBs awaiting
 						# selection
-SCB_LIST_NULL	= 0x10
+SCB_LIST_NULL	= 0xff
 
 
 #  Poll QINCNT for work - the lower bits contain
 #  the number of entries in the Queue In FIFO.
 #
-start:
-	test	WAITING_SCBH,SCB_LIST_NULL jz start_waiting
 poll_for_work:
 	test	FLAGS,TWIN_BUS	jz start2	# Are we a twin channel device?
 # For fairness, we check the other bus first, since we just finished a 
 # transaction on the current channel.
 	xor	SBLKCTL,0x08			# Toggle to the other bus
 	test	SSTAT0,SELDI	jnz reselect
-	test	SSTAT0,SELDO	jnz select
 	xor	SBLKCTL,0x08			# Toggle to the original bus
 start2:
 	test	SSTAT0,SELDI	jnz reselect
-	test	SSTAT0,SELDO	jnz select
-	test	WAITING_SCBH,SCB_LIST_NULL jz start_waiting
+	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting
 	test	QINCNT,SCBMASK	jz poll_for_work
 
 # We have at least one queued SCB now and we don't have any 
@@ -300,15 +296,10 @@
 # Copy the SCB from the FIFO to  the SCBARRAY
 
 	mvi	DINDEX, SCBARRAY+0
-	call	bcopy_3_dfdat 
-	call	bcopy_4_dfdat
-	call	bcopy_4_dfdat
-	call	bcopy_4_dfdat   
-	call	bcopy_4_dfdat
-# ifndef AIC7XXX_USE_SG
-#	call	bcopy_3_dfdat
-#	call	bcopy_4_dfdat
-# endif
+	call	bcopy_5_dfdat 
+	call	bcopy_7_dfdat
+	call	bcopy_7_dfdat
+	call	bcopy_7_dfdat   
 
 # See if there is not already an active SCB for this target.  This code
 # locks out on a per target basis instead of target/lun.  Although this
@@ -320,12 +311,12 @@
 # initialization, board reset, and a target's SELTO.
 
 test_busy:
-	test	SCBARRAY+0,0x20	jnz start_scb
 	and	FUNCTION1,0x70,SCBARRAY+1
 	mov	A,FUNCTION1
 	test	SCBARRAY+1,0x88	jz test_a	# Id < 8 && A channel
 
 	test	ACTIVE_B,A	jnz requeue
+	test	SCBARRAY+0,TAG_ENB	jnz start_scb
 	or	ACTIVE_B,A	# Mark the current target as busy
 	jmp	start_scb
 
@@ -341,6 +332,7 @@
 
 test_a:
 	test	ACTIVE_A,A	jnz requeue
+	test	SCBARRAY+0,TAG_ENB	jnz start_scb
 	or	ACTIVE_A,A	# Mark the current target as busy
 
 start_scb:
@@ -358,8 +350,7 @@
 start_selection:
 	or	SCSISEQ,0x48			# ENSELO|ENAUTOATNO
 	mov	WAITING_SCBH, SCBPTR
-	clr	SG_NOLOAD
-	and	FLAGS,0x3f	# !RESELECTING
+	and	FLAGS,0x3f	# !RESELECTING 
 
 #  As soon as we get a successful selection, the target should go
 #  into the message out phase since we have ATN asserted.  Prepare
@@ -378,17 +369,17 @@
 #  so we interrupt the driver, allow it to fill the message buffer, and
 #  then go back into the arbitration loop
 	mvi     INTSTAT,AWAITING_MSG
-	jmp     poll_for_work
+	jmp     wait_for_selection
 
 identify:
-	mov	SCBARRAY+1	call disconnect	# disconnect ok?
+	and	A,DISCENB,SCBARRAY+0		# mask off disconnect privledge
 
 	and	SINDEX,0x7,SCBARRAY+1		# lun
-	or	SINDEX,A			# return value from disconnect
+	or	SINDEX,A			# or in disconnect privledge
 	or	SINDEX,0x80	call mk_mesg	# IDENTIFY message
 
 	mov	A,SINDEX
-	test	SCBARRAY+0,0xe0	jz  !message	# WDTR, SDTR or TAG??
+	test	SCBARRAY+0,0xb0	jz  !message	# WDTR, SDTR or TAG??
 	cmp	MSG_START+0,A	jne !message	# did driver beat us?
 
 # Tag Message if Tag enabled in SCB control block.  Use SCBPTR as the tag
@@ -408,7 +399,10 @@
 	mov	DINDEX	call mk_dtr	# build DTR message if needed
 
 !message:
-	jmp	poll_for_work
+wait_for_selection:
+	test	SSTAT0,SELDI	jnz reselect
+	test	SSTAT0,SELDO	jnz select
+	jmp	wait_for_selection
 
 #  Reselection has been initiated by a target. Make a note that we've been
 #  reselected, but haven't seen an IDENTIFY message from the target
@@ -461,78 +455,118 @@
 
 p_dataout:
 	mvi	0		call scsisig	# !CDO|!IOO|!MSGO
-	call	assert
-	call	sg_load
+	mvi	DMAPARAMS,0x7d			# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
+						#   DIRECTION|FIFORESET
+	jmp	data_phase_init
 
-	mvi	DINDEX,HADDR
-	mvi	SCBARRAY+19	call bcopy_4
+# If we re-enter the data phase after going through another phase, the
+# STCNT may have been cleared, so restore it from the residual field.
+data_phase_reinit:
+	mvi	DINDEX, STCNT
+	mvi	SCBARRAY+15	call bcopy_3
+	jmp	data_phase_loop
 
-#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
-	mvi	SCBARRAY+23	call bcopy_3
+# Reads should not use WIDEODD since it may make the last byte for a SG segment
+# go to the next segment.
+p_datain:
+	mvi	0x40		call scsisig	# !CDO|IOO|!MSGO
+	mvi	DMAPARAMS,0x39			# SCSIEN|SDMAEN|HDMAEN|
+						#   !DIRECTION|FIFORESET
+data_phase_init:
+	call	assert
 
-	mvi	DINDEX,STCNT
-	mvi	SCBARRAY+23	call bcopy_3
+	test	FLAGS, DPHASE	jnz data_phase_reinit
+	call	sg_scb2ram
+	or	FLAGS, DPHASE			# We have seen a data phase
 
+data_phase_loop:
 # If we are the last SG block, don't set wideodd.
-	test    SCBARRAY+18,0xff jnz p_dataout_wideodd
-	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
-						#   DIRECTION|FIFORESET
-	jmp	p_dataout_rest
+	cmp	SG_COUNT,0x01 jne data_phase_wideodd
+	and	DMAPARAMS, 0xbf		        # Turn off WIDEODD 
+data_phase_wideodd:
+	mov	DMAPARAMS  call dma
 
-p_dataout_wideodd:
-	mvi	0xbd		call dma	# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-						#   DIRECTION|FIFORESET
+# Exit if we had an underrun
+	test	SSTAT0,0x04	jz data_phase_finish	# underrun STCNT != 0
 
-p_dataout_rest:
-#  After a DMA finishes, save the final transfer pointer and count
-#  back into the SCB, in case a device disconnects in the middle of
-#  a transfer.  Use SHADDR and STCNT instead of HADDR and HCNT, since
-#  it's a reflection of how many bytes were transferred on the SCSI
-#  (as opposed to the host) bus.
+#  Advance the scatter-gather pointers if needed 
 #
-	mvi	DINDEX,SCBARRAY+23
-	mvi	STCNT		call bcopy_3
-
-	mvi	DINDEX,SCBARRAY+19
-	mvi	SHADDR		call bcopy_4
+sg_advance:
+	dec	SG_COUNT			# one less segment to go
 
-	call	sg_advance
-	mov	SCBARRAY+18,SG_COUNT		# residual S/G count
+	test	SG_COUNT, 0xff	jz data_phase_finish #Are we done?
 
-	jmp	ITloop
+	clr	A				# add sizeof(struct scatter)
+	add	SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
+	adc	SG_NEXT+1,A,SG_NEXT+1
+	adc	SG_NEXT+2,A,SG_NEXT+2
+	adc	SG_NEXT+3,A,SG_NEXT+3
 
-p_datain:
-	mvi	0x40		call scsisig	# !CDO|IOO|!MSGO
-	call	assert
-	call	sg_load
+#  Load a struct scatter and set up the data address and length.
+#  If the working value of the SG count is nonzero, then
+#  we need to load a new set of values.
+#
+#  This, like all DMA's, assumes a little-endian host data storage.
+#
+sg_load:
+	clr	HCNT+2
+	clr	HCNT+1
+	mvi	HCNT+0,SG_SIZEOF
 
 	mvi	DINDEX,HADDR
-	mvi	SCBARRAY+19	call bcopy_4
+	mvi	SG_NEXT		call bcopy_4
 
-#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
-	mvi	SCBARRAY+23	call bcopy_3
+	mvi	DFCNTRL,0xd			# HDMAEN|DIRECTION|FIFORESET
 
-	mvi	DINDEX,STCNT
-	mvi	SCBARRAY+23	call bcopy_3
+#  Wait for DMA from host memory to data FIFO to complete, then disable
+#  DMA and wait for it to acknowledge that it's off.
+#
+	call	dma_finish
 
-# If we are the last SG block, don't set wideodd.
-	test	SCBARRAY+18,0xff jnz p_datain_wideodd
-	mvi	0x39		call dma	# SCSIEN|SDMAEN|HDMAEN|
-						#   !DIRECTION|FIFORESET
-	jmp	p_datain_rest
-p_datain_wideodd:
-	mvi	0xb9		call dma	# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-						#   !DIRECTION|FIFORESET
-p_datain_rest:
-	mvi	DINDEX,SCBARRAY+23
-	mvi	STCNT		call bcopy_3
+#  Copy data from FIFO into SCB data pointer and data count.  This assumes
+#  that the struct scatterlist has this structure (this and sizeof(struct
+#  scatterlist) == 12 are asserted in aic7xxx.c):
+#
+#	struct scatterlist {
+#		char *address;		/* four bytes, little-endian order */
+#		...			/* four bytes, ignored */
+#		unsigned short length;	/* two bytes, little-endian order */
+#	}
+#
 
-	mvi	DINDEX,SCBARRAY+19
-	mvi	SHADDR		call bcopy_4
+# Not in FreeBSD.  the scatter list entry is only 8 bytes.
+# 
+# struct ahc_dma_seg {
+#       physaddr addr;                  /* four bytes, little-endian order */
+#       long    len;                    /* four bytes, little endian order */   
+# };
+#
+
+	mvi	DINDEX,HADDR
+#	call	bcopy_7_dfdat
+
+# For Linux, we must throw away four bytes since there is a 32bit gap
+# in the middle of a struct scatterlist
+	call	bcopy_4_dfdat
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+	call	bcopy_3_dfdat		#Only support 24 bit length.
 
-	call	sg_advance
-	mov	SCBARRAY+18,SG_COUNT		# residual S/G count
+# Load STCNT as well.  It is a mirror of HCNT
+	mvi	DINDEX,STCNT
+	mvi	HCNT	call bcopy_3
+        test    SSTAT1,PHASEMIS  jz data_phase_loop
 
+data_phase_finish:
+#  After a DMA finishes, save the SG and STCNT residuals back into the SCB
+#  We use STCNT instead of HCNT, since it's a reflection of how many bytes 
+#  were transferred on the SCSI (as opposed to the host) bus.
+#
+	mvi	DINDEX,SCBARRAY+15
+	mvi	STCNT		call bcopy_3
+	mov	SCBARRAY+18, SG_COUNT
 	jmp	ITloop
 
 #  Command phase.  Set up the DMA registers and let 'er rip - the
@@ -543,11 +577,9 @@
 	mvi	0x80		call scsisig	# CDO|!IOO|!MSGO
 	call	assert
 
+# Load HADDR and HCNT.  We can do this in one bcopy since they are neighbors
 	mvi	DINDEX,HADDR
-	mvi	SCBARRAY+7	call bcopy_4
-
-#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
-	mvi	SCBARRAY+11	call bcopy_3
+	mvi	SCBARRAY+7	call bcopy_7
 
 	mvi	DINDEX,STCNT
 	mvi	SCBARRAY+11	call bcopy_3
@@ -563,7 +595,7 @@
 	mvi	0xc0		call scsisig	# CDO|IOO|!MSGO
 
 	mvi	SCBARRAY+14	call inb_first
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  Message out phase.  If there is no active message, but the target
 #  took us into this phase anyway, build a no-op message and send it.
@@ -651,8 +683,34 @@
 	mvi	A		call inb_first	# read the 1st message byte
 	mvi	REJBYTE,A			# save it for the driver
 
-	cmp	ALLZEROS,A	jne p_mesgin1
+	test	A,0x80		jnz mesgin_identify	# identify message?
+	cmp	A,4		je mesgin_disconnect	# disconnect?
+	cmp	A,2		je mesgin_sdptrs	# save data pointers?
+	cmp	ALLZEROS,A	je mesgin_complete	# command complete?
+	cmp	A,3		je mesgin_rdptrs	# restore pointers code?
+	cmp	A,1		je mesgin_extended	# extended message?
+	cmp	A,7		je mesgin_reject	# message reject code?
+
+rej_mesgin:
+#  We have no idea what this message in is, and there's no way
+#  to pass it up to the kernel, so we issue a message reject and
+#  hope for the best.  Since we're now using manual PIO mode to
+#  read in the message, there should no longer be a race condition
+#  present when we assert ATN.  In any case, rejection should be a
+#  rare occurrence - signal the driver when it happens.
+#
+	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
+	call	scsisig
+	mvi	INTSTAT,SEND_REJECT		# let driver know
+
+	mvi	0x7		call mk_mesg	# MESSAGE REJECT message
+
+mesgin_done:
+	call	inb_last			# ack & turn auto PIO back on
+	jmp	ITloop
+
 
+mesgin_complete:
 #  We got a "command complete" message, so put the SCB pointer
 #  into the Queue Out, and trigger a completion interrupt.
 #  Check status for non zero return and interrupt driver if needed
@@ -669,19 +727,17 @@
 #  before the command complete code tried processing it.
 
 # First check for residuals
-	test	SCBARRAY+15,0xff	jnz resid
-	test	SCBARRAY+16,0xff	jnz resid
-	test	SCBARRAY+17,0xff	jnz resid
+	test	SCBARRAY+18,0xff	jnz resid
 
 check_status:
 	test	SCBARRAY+14,0xff	jz status_ok	# 0 Status?
 	mvi	INTSTAT,BAD_STATUS			# let driver know
 	test	RETURN_1, 0x80	jz status_ok
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 status_ok:
 #  First, mark this target as free.
-	test	SCBARRAY+0,0x20	jnz complete		# Tagged command
+	test	SCBARRAY+0,TAG_ENB	jnz complete		# Tagged command
 	and	FUNCTION1,0x70,SCBARRAY+1
 	mov	A,FUNCTION1
 	test	SCBARRAY+1,0x88 jz clear_a
@@ -691,11 +747,16 @@
 clear_a:
 	xor	ACTIVE_A,A
 
+	test    SCBARRAY+11,0xff jnz complete  # Immediate message complete
+# Pause the sequencer until the driver gets around to handling the command
+# complete.  This is so that any action that might require carefull timing
+# with the completion of this command can occur.
+	mvi	INTSTAT,IMMEDDONE
+	jmp	poll_for_work
 complete:
 	mov	QOUTFIFO,SCBPTR
 	mvi	INTSTAT,CMDCMPLT
-	test    SCBARRAY+11,0xff jz start	# Immediate message complete
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 # If we have a residual count, interrupt and tell the host.  Other
 # alternatives are to pause the sequencer on all command completes (yuck),
@@ -714,21 +775,19 @@
 #  apparently this can be done after any message in byte, according
 #  to the SCSI-2 spec.
 #
-p_mesgin1:
-	cmp	A,1		jne p_mesgin2	# extended message code?
-	
+mesgin_extended:
 	mvi	ARG_1		call inb_next	# extended message length
 	mvi	A		call inb_next	# extended message code
 
 	cmp	A,1		je p_mesginSDTR	# Syncronous negotiation message
 	cmp	A,3		je p_mesginWDTR # Wide negotiation message
-	jmp	p_mesginN
+	jmp	rej_mesgin
 
 p_mesginWDTR:
-	cmp	ARG_1,2		jne p_mesginN	# extended mesg length = 2
+	cmp	ARG_1,2		jne rej_mesgin	# extended mesg length=2
 	mvi	A		call inb_next	# Width of bus
 	mvi	INTSTAT,MSG_WDTR		# let driver know
-	test	RETURN_1,0x80	jz p_mesgin_done# Do we need to send WDTR?
+	test	RETURN_1,0x80	jz mesgin_done# Do we need to send WDTR?
 
 # We didn't initiate the wide negotiation, so we must respond to the request
 	and	RETURN_1,0x7f			# Clear the SEND_WDTR Flag
@@ -737,59 +796,53 @@
 	mvi	MSG_START+0	call mk_wdtr	# build WDTR message	
 	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
 	call	scsisig
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 p_mesginSDTR:
-	cmp	ARG_1,3		jne p_mesginN	# extended mesg length = 3
+	cmp	ARG_1,3		jne rej_mesgin	# extended mesg length=3
 	mvi	ARG_1		call inb_next	# xfer period
 	mvi	A		call inb_next	# REQ/ACK offset
 	mvi	INTSTAT,MSG_SDTR		# call driver to convert
 
-	test	RETURN_1,0xc0	jz p_mesgin_done# Do we need to mk_sdtr or rej?
-	test	RETURN_1,0x40	jnz p_mesginN	# Requested SDTR too small - rej
+	test	RETURN_1,0xc0	jz mesgin_done# Do we need to mk_sdtr or rej?
+	test	RETURN_1,0x40	jnz rej_mesgin	# Requested SDTR too small - rej
 	or	FLAGS,ACTIVE_MSG
 	mvi	DINDEX, MSG_START+0
 	mvi     MSG_START+0     call mk_sdtr
 	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
 	call	scsisig
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  Is it a disconnect message?  Set a flag in the SCB to remind us
 #  and await the bus going free.
 #
-p_mesgin2:
-	cmp	A,4		jne p_mesgin3	# disconnect code?
-
-	or	SCBARRAY+0,0x4			# set "disconnected" bit
-	jmp	p_mesgin_done
+mesgin_disconnect:
+	or	SCBARRAY+0,DISCONNECTED
+	jmp	mesgin_done
 
 #  Save data pointers message?  Copy working values into the SCB,
 #  usually in preparation for a disconnect.
 #
-p_mesgin3:
-	cmp	A,2		jne p_mesgin4	# save data pointers code?
-
+mesgin_sdptrs:
 	call	sg_ram2scb
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  Restore pointers message?  Data pointers are recopied from the
-#  SCB anyway at the start of any DMA operation, so the only thing
-#  to copy is the scatter-gather values.
-#
-p_mesgin4:
-	cmp	A,3		jne p_mesgin5	# restore pointers code?
-
-	call	sg_scb2ram
-	jmp	p_mesgin_done
+#  SCB anytime we enter a data phase for the first time, so all
+#  we need to do is clear the DPHASE flag and let the data phase
+#  code do the rest.
+#
+mesgin_rdptrs:
+	and	FLAGS,0xfb			# !DPHASE we'll reload them
+						#   the next time through
+	jmp	mesgin_done
 
 #  Identify message?  For a reconnecting target, this tells us the lun
 #  that the reconnection is for - find the correct SCB and switch to it,
 #  clearing the "disconnected" bit so we don't "find" it by accident later.
 #
-p_mesgin5:
-	test	A,0x80		jz p_mesgin6	# identify message?
-
-	test	A,0x78		jnz p_mesginN	# !DiscPriv|!LUNTAR|!Reserved
+mesgin_identify:
+	test	A,0x78		jnz rej_mesgin	# !DiscPriv|!LUNTAR|!Reserved
 
 	and	A,0x07				# lun in lower three bits
 	or      SAVED_TCL,A,SELID          
@@ -802,8 +855,6 @@
 	and	SCBARRAY+0,0xfb			# clear disconnect bit in SCB
 	or	FLAGS,IDENTIFY_SEEN		# make note of IDENTIFY
 
-	call	sg_scb2ram			# implied restore pointers
-						#   required on reselect
 	jmp	ITloop
 get_tag:
 	mvi	A		call inb_first
@@ -828,34 +879,12 @@
 #  the target selecting 8bit or asynchronous transfer, otherwise just ignore 
 #  it since we have no clue what it pertains to.
 #
-p_mesgin6:
-	cmp	A,7		jne p_mesgin7	# message reject code?
-
+mesgin_reject:
 	mvi	INTSTAT, MSG_REJECT
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  [ ADD MORE MESSAGE HANDLING HERE ]
 #
-p_mesgin7:
-
-#  We have no idea what this message in is, and there's no way
-#  to pass it up to the kernel, so we issue a message reject and
-#  hope for the best.  Since we're now using manual PIO mode to
-#  read in the message, there should no longer be a race condition
-#  present when we assert ATN.  In any case, rejection should be a
-#  rare occurrence - signal the driver when it happens.
-#
-p_mesginN:
-	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
-	call	scsisig
-	mvi	INTSTAT,SEND_REJECT		# let driver know
-
-	mvi	0x7		call mk_mesg	# MESSAGE REJECT message
-
-p_mesgin_done:
-	call	inb_last			# ack & turn auto PIO back on
-	jmp	ITloop
-
 
 #  Bus free phase.  It might be useful to interrupt the device
 #  driver if we aren't expecting this.  For now, make sure that
@@ -868,33 +897,34 @@
 #  if this is an immediate command, perform a psuedo command complete to
 #  notify the driver.
 	test	SCBARRAY+11,0xff	jz status_ok
-	jmp	start
+	jmp	poll_for_work
 
 #  Instead of a generic bcopy routine that requires an argument, we unroll
-#  the two cases that are actually used, and call them explicitly.  This
-#  not only reduces the overhead of doing a bcopy by 2/3rds, but ends up
-#  saving space in the program since you don't have to put the argument 
-#  into the accumulator before the call.  Both functions expect DINDEX to
-#  contain the destination address and SINDEX to contain the source 
-#  address.
-bcopy_3:
+#  the cases that are actually used, and call them explicitly.  This
+#  not only reduces the overhead of doing a bcopy, but ends up saving space 
+#  in the program since you don't have to put the argument into the accumulator
+#  before the call.  Both functions expect DINDEX to contain the destination 
+#  address and SINDEX to contain the source address.
+bcopy_7:
 	mov	DINDIR,SINDIR
 	mov	DINDIR,SINDIR
-	mov	DINDIR,SINDIR	ret
-
+bcopy_5:
+	mov	DINDIR,SINDIR
 bcopy_4:
 	mov	DINDIR,SINDIR
+bcopy_3:
 	mov	DINDIR,SINDIR
 	mov	DINDIR,SINDIR
 	mov	DINDIR,SINDIR	ret
 	
-bcopy_3_dfdat:
+bcopy_7_dfdat:
 	mov	DINDIR,DFDAT
 	mov	DINDIR,DFDAT
-	mov	DINDIR,DFDAT	ret
-
+bcopy_5_dfdat:
+	mov	DINDIR,DFDAT
 bcopy_4_dfdat:
 	mov	DINDIR,DFDAT
+bcopy_3_dfdat:
 	mov	DINDIR,DFDAT
 	mov	DINDIR,DFDAT
 	mov	DINDIR,DFDAT	ret
@@ -962,7 +992,6 @@
 dma:
 	mov	DFCNTRL,SINDEX
 dma1:
-dma2:
 	test	SSTAT0,0x1	jnz dma3	# DMADONE
 	test	SSTAT1,0x10	jz dma1		# PHASEMIS, ie. underrun
 
@@ -978,19 +1007,14 @@
 dma4:
 	test	DFSTATUS,0x1	jz dma4		# !FIFOEMP
 
-#  Now shut the DMA enables off, and copy STCNT (ie. the underrun
-#  amount, if any) to the SCB registers; SG_COUNT will get copied to
-#  the SCB's residual S/G count field after sg_advance is called.  Make
-#  sure that the DMA enables are actually off first lest we get an ILLSADDR.
+#  Now shut the DMA enables off and make sure that the DMA enables are 
+#  actually off first lest we get an ILLSADDR.
 #
 dma5:
 	clr	DFCNTRL				# disable DMA
 dma6:
 	test	DFCNTRL,0x38	jnz dma6	# SCSIENACK|SDMAENACK|HDMAENACK
 
-	mvi	DINDEX,SCBARRAY+15
-	mvi	STCNT		call bcopy_3
-
 	ret
 
 dma_finish:
@@ -1022,10 +1046,9 @@
 
 	mvi	SXFRCTL0,0x8a			# DFON|SPIOEN|CLRCHN
 
-#  Initialize scatter-gather pointers by setting up the working copy
-#  in scratch RAM.
-#
-	call	sg_scb2ram
+#  Make sure that the system knows we have not been through a DATA
+#  phase.
+	and	FLAGS, 0xfb			# !DPHASE
 
 #  Initialize SCSIRATE with the appropriate value for this target.
 #
@@ -1041,29 +1064,6 @@
 
 	mvi	INTSTAT,NO_IDENT 	ret	# no - cause a kernel panic
 
-#  Find out if disconnection is ok from the information the BIOS has left
-#  us.  The tcl from SCBARRAY+1 should be in SINDEX; A will
-#  contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok)
-#  on exit.
-#
-#  To allow for wide or twin busses, we check the upper bit of the target ID
-#  and the channel ID and look at the appropriate disconnect register. 
-#
-disconnect:
-	and	FUNCTION1,0x70,SINDEX		# strip off extra just in case
-	mov	A,FUNCTION1
-	test	SINDEX, 0x88	jz disconnect_a
-
-	test	DISC_DSB_B,A	jz disconnect1	# bit nonzero if DISabled
-	clr	A		ret
-
-disconnect_a:
-	test	DISC_DSB_A,A	jz disconnect1	# bit nonzero if DISabled
-	clr	A		ret
-
-disconnect1:
-	mvi	A,0x40		ret
-
 #  Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch 
 #  the SCB to it.  Have the kernel print a warning message if it can't be 
 #  found, and generate an ABORT message to the target.  SINDEX should be
@@ -1073,7 +1073,7 @@
 	mov	A,SAVED_TCL
 	mov	SCBPTR,SINDEX			# switch to new SCB
 	cmp	SCBARRAY+1,A	jne findSCB1	# target ID/channel/lun match?
-	test	SCBARRAY+0,0x4	jz findSCB1	# should be disconnected
+	test	SCBARRAY+0,DISCONNECTED	jz findSCB1 # should be disconnected
 	test	SCBARRAY+0,TAG_ENB jnz get_tag
 	ret
 
@@ -1089,113 +1089,40 @@
 	call	scsisig
 	ret
 
-#  Make a working copy of the scatter-gather parameters in the SCB.
+#  Make a working copy of the scatter-gather parameters from the SCB.
 #
 sg_scb2ram:
+	mvi	DINDEX,HADDR
+	mvi	SCBARRAY+19	call bcopy_7
+
+	mvi	DINDEX,STCNT
+	mvi	SCBARRAY+23	call bcopy_3
+
 	mov	SG_COUNT,SCBARRAY+2
 
 	mvi	DINDEX,SG_NEXT
 	mvi	SCBARRAY+3	call bcopy_4
+	ret
 
-	mvi	SG_NOLOAD,0x80
-	test	SCBARRAY+0,0x10	jnz return	# don't reload s/g?
-	clr	SG_NOLOAD	 ret
-
-#  Copying RAM values back to SCB, for Save Data Pointers message.
+#  Copying RAM values back to SCB, for Save Data Pointers message, but
+#  only if we've actually been into a data phase to change them.  This
+#  protects against bogus data in scratch ram and the residual counts
+#  since they are only initialized when we go into data_in or data_out.
 #
 sg_ram2scb:
+	test	FLAGS, DPHASE	jz return
 	mov	SCBARRAY+2,SG_COUNT
 
 	mvi	DINDEX,SCBARRAY+3
 	mvi	SG_NEXT		call bcopy_4
+	
+	mvi	DINDEX,SCBARRAY+19
+	mvi	SHADDR	call bcopy_4
 
-	and	SCBARRAY+0,0xef,SCBARRAY+0
-	test	SG_NOLOAD,0x80	jz return	# reload s/g?
-	or	SCBARRAY+0,SG_LOAD	 ret
-
-#  Load a struct scatter if needed and set up the data address and
-#  length.  If the working value of the SG count is nonzero, then
-#  we need to load a new set of values.
-#
-#  This, like the above DMA, assumes a little-endian host data storage.
-#
-sg_load:
-	test	SG_COUNT,0xff	jz return	# SG being used?
-	test	SG_NOLOAD,0x80	jnz return	# don't reload s/g?
-
-	clr	HCNT+2
-	clr	HCNT+1
-	mvi	HCNT+0,SG_SIZEOF
-
-	mvi	DINDEX,HADDR
-	mvi	SG_NEXT		call bcopy_4
-
-	mvi	DFCNTRL,0xd			# HDMAEN|DIRECTION|FIFORESET
-
-#  Wait for DMA from host memory to data FIFO to complete, then disable
-#  DMA and wait for it to acknowledge that it's off.
-#
-
-	call	dma_finish
-
-#  Copy data from FIFO into SCB data pointer and data count.  This assumes
-#  that the struct scatterlist has this structure (this and sizeof(struct
-#  scatterlist) == 12 are asserted in aic7xxx.c):
-#
-#	struct scatterlist {
-#		char *address;		/* four bytes, little-endian order */
-#		...			/* four bytes, ignored */
-#		unsigned short length;	/* two bytes, little-endian order */
-#	}
-#
-
-# Not in FreeBSD.  the scatter list entry is only 8 bytes.
-# 
-# struct ahc_dma_seg {
-#       physaddr addr;                  /* four bytes, little-endian order */
-#       long    len;                    /* four bytes, little endian order */   
-# };
-#
-
-	mvi	DINDEX, SCBARRAY+19
-	call	bcopy_4_dfdat
-
-# For Linux, we must throw away four bytes since there is a 32bit gap
-# in the middle of a struct scatterlist
-	mov	NONE,DFDAT
-	mov	NONE,DFDAT
-	mov	NONE,DFDAT
-	mov	NONE,DFDAT
-
-	call	bcopy_3_dfdat		#Only support 24 bit length.
+# Use the residual number since STCNT is corrupted by any message transfer
+	mvi	SCBARRAY+15	call bcopy_3
 	ret
 
-#  Advance the scatter-gather pointers only IF NEEDED.  If SG is enabled,
-#  and the SCSI transfer count is zero (note that this should be called
-#  right after a DMA finishes), then move the working copies of the SG
-#  pointer/length along.  If the SCSI transfer count is not zero, then
-#  presumably the target is disconnecting - do not reload the SG values
-#  next time.
-#
-sg_advance:
-	test	SG_COUNT,0xff	jz return	# s/g enabled?
-
-	test	STCNT+0,0xff	jnz sg_advance1	# SCSI transfer count nonzero?
-	test	STCNT+1,0xff	jnz sg_advance1
-	test	STCNT+2,0xff	jnz sg_advance1
-
-	clr	SG_NOLOAD			# reload s/g next time
-	dec	SG_COUNT			# one less segment to go
-
-	clr	A				# add sizeof(struct scatter)
-	add	SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
-	adc	SG_NEXT+1,A,SG_NEXT+1
-	adc	SG_NEXT+2,A,SG_NEXT+2
-	adc	SG_NEXT+3,A,SG_NEXT+3	ret
-
-sg_advance1:
-	mvi	SG_NOLOAD,0x80	ret		# don't reload s/g next time
-
 #  Add the array base SYNCNEG to the target offset (the target address
 #  is in SCSIID), and return the result in SINDEX.  The accumulator
 #  contains the 3->8 decoding of the target ID on return.
@@ -1217,7 +1144,7 @@
 #  reject, you wouldn't be able to tell which message was the culpret.
 #
 mk_dtr:
-	test	SCBARRAY+0,0xc0 jz return	# NEEDWDTR|NEEDSDTR
+	test	SCBARRAY+0,0x90 jz return	# NEEDWDTR|NEEDSDTR
 	test	SCBARRAY+0,NEEDWDTR jnz  mk_wdtr_16bit
 	or	FLAGS, MAX_OFFSET	# Force an offset of 15 or 8 if WIDE
 
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx_asm.c linux/drivers/scsi/aic7xxx_asm.c
--- v1.3.35/linux/drivers/scsi/aic7xxx_asm.c	Wed Aug  2 13:21:07 1995
+++ linux/drivers/scsi/aic7xxx_asm.c	Mon Oct 23 13:21:53 1995
@@ -27,7 +27,7 @@
  * A <label> is an <undef-sym> ending in a colon.  Spaces, tabs, and commas
  * are token separators.
  *-M*************************************************************************/
-static char id[] = "$Id: aic7xxx_asm.c,v 2.0 1995/08/02 05:28:42 deang Exp $";
+static char id[] = "$Id: aic7xxx_asm.c,v 2.1 1995/08/23 04:31:40 deang Exp $";
 #include <ctype.h>
 #include <stdio.h>
 #include <string.h>
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v1.3.35/linux/drivers/scsi/aic7xxx_proc.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/aic7xxx_proc.c	Mon Oct 23 13:21:53 1995
@@ -0,0 +1,275 @@
+/*+M*************************************************************************
+ * Adaptec 274x/284x/294x device driver proc support for Linux.
+ *
+ * Copyright (c) 1995 Dean W. Gehnert
+ *
+ * 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.
+ *
+ * ----------------------------------------------------------------
+ *  o Modified from the EATA /proc support.
+ *  o Additional support for device block statistics provided by
+ *    Matthew Jacob.
+ *
+ *  Dean W. Gehnert, deang@ims.com, 08/30/95
+ *
+ *  $Id: aic7xxx_proc.c,v 2.2 1995/09/12 05:24:37 deang Exp $
+ *-M*************************************************************************/
+
+#define BLS buffer + len + size
+#define HDRB \
+"        < 512 512-1K   1-2K   2-4K   4-8K  8-16K 16-32K 32-64K 64-128K >128K"
+
+#ifdef PROC_DEBUG
+extern int vsprintf(char *, const char *, va_list);
+
+static void
+proc_debug(const char *fmt, ...)
+{
+  va_list ap;
+  char buf[256];
+
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  printk(buf);
+  va_end(ap);
+}
+#else /* PROC_DEBUG */
+#  define proc_debug(fmt, args...)
+#endif /* PROC_DEBUG */
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_set_info
+ *
+ * Description:
+ *   Set parameters for the driver from the /proc filesystem.
+ *-F*************************************************************************/
+int
+aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
+{
+  proc_debug("aic7xxx_set_info(): %s\n", buffer);
+  return (-ENOSYS);  /* Currently this is a no-op */
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_proc_info
+ *
+ * Description:
+ *   Return information to handle /proc support for the driver.
+ *-F*************************************************************************/
+int
+aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length, 
+    int hostno, int inout)
+{
+  struct Scsi_Host *HBAptr;
+  struct aic7xxx_host *p;
+  static u8 buff[512];
+  int   i; 
+  int   size = 0;
+  int   len = 0;
+  off_t begin = 0;
+  off_t pos = 0;
+  static char *board_name[] = {"None", "274x", "284x", "7870", "7850", "7872"};
+  static char *bus_name[] = {"Single", "Twin", "Wide"};
+
+  HBAptr = NULL;
+  for (i = 0; i < NUMBER(aic7xxx_boards); i++)
+  {
+    if ((HBAptr = aic7xxx_boards[i]) != NULL)
+    {
+      if (HBAptr->host_no == hostno)
+      {
+        break;
+      }
+
+      while ((HBAptr->hostdata != NULL) &&
+          ((HBAptr = ((struct aic7xxx_host *) HBAptr->hostdata)->next) != NULL))
+      {
+        if (HBAptr->host_no == hostno)
+        {
+          break; break;
+        }
+      }
+
+      HBAptr = NULL;
+    }
+  }
+
+  if (HBAptr == NULL)
+  {
+    size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno);
+    len += size; pos = begin + len; size = 0;
+    goto stop_output;
+  }
+
+  if (inout == TRUE) /* Has data been written to the file? */ 
+  {
+    return (aic7xxx_set_info(buffer, length, HBAptr));
+  }
+
+  if (offset == 0)
+  {
+    memset(buff, 0, sizeof(buff));
+  }
+
+  p = (struct aic7xxx_host *) HBAptr->hostdata;
+
+  size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
+  size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
+  size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_H_VERSION));
+  size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
+  len += size; pos = begin + len; size = 0;
+
+  size += sprintf(BLS, "\n");
+  size += sprintf(BLS, "Compile Options:\n");
+#ifdef AIC7XXX_RESET_DELAY
+  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
+#endif
+#ifdef AIC7XXX_TWIN_SUPPORT
+  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Enabled\n");
+#else
+  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Disabled\n");
+#endif
+#ifdef AIC7XXX_TAGGED_QUEUEING
+  size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Enabled\n");
+#else
+  size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Disabled\n");
+#endif
+#ifdef AIC7XXX_SHARE_IRQS
+  size += sprintf(BLS, "  AIC7XXX_SHARE_IRQS     : Enabled\n");
+#else
+  size += sprintf(BLS, "  AIC7XXX_SHARE_IRQS     : Disabled\n");
+#endif
+#ifdef AIC7XXX_PROC_STATS
+  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
+#else
+  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
+#endif
+#ifdef AIC7XXX_POLL
+  size += sprintf(BLS, "  AIC7XXX_POLL           : Enabled\n");
+#else
+  size += sprintf(BLS, "  AIC7XXX_POLL           : Disabled\n");
+#endif
+  len += size; pos = begin + len; size = 0;
+
+  size += sprintf(BLS, "\n");
+  size += sprintf(BLS, "Adapter Configuration:\n");
+  size += sprintf(BLS, "          SCSI Adapter: %s\n", board_name[p->type]);
+  size += sprintf(BLS, "              Host Bus: %s\n", bus_name[p->bus_type]);
+  size += sprintf(BLS, "               Base IO: %#.4x\n", p->base);
+  size += sprintf(BLS, "                   IRQ: %d\n", HBAptr->irq);
+  size += sprintf(BLS, "                   SCB: %d (%d)\n", p->numscb, p->maxscb);
+  size += sprintf(BLS, "            Interrupts: %d", p->isr_count);
+  if ((p->type == AIC_274x) || (p->type == AIC_284x))
+  {
+    size += sprintf(BLS, " %s\n",
+        (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
+  }
+  else
+  {
+    size += sprintf(BLS, "\n");
+  }
+  size += sprintf(BLS, "         Serial EEPROM: %s\n",
+      p->have_seeprom ? "True" : "False");
+  size += sprintf(BLS, "         Pause/Unpause: %#.2x/%#.2x\n", p->pause,
+      p->unpause);
+  size += sprintf(BLS, "  Extended Translation: %sabled\n",
+      p->extended ? "En" : "Dis");
+  size += sprintf(BLS, "        SCSI Bus Reset: %sabled\n",
+      aic7xxx_no_reset ? "Dis" : "En");
+  len += size; pos = begin + len; size = 0;
+
+#ifdef AIC7XXX_PROC_STATS
+  {
+    struct aic7xxx_xferstats *sp;
+    int channel, target, lun;
+
+    /*
+     * XXX: Need to fix this to avoid overflow...
+     */
+    size += sprintf(BLS, "\n");
+    size += sprintf(BLS, "Statistics:\n");
+    for (channel = 0; channel < 2; channel++)
+    {
+      for (target = 0; target < 16; target++)
+      {
+        for (lun = 0; lun < 8; lun++)
+        {
+          sp = &p->stats[channel][target][lun];
+          if (sp->xfers == 0)
+          {
+            continue;
+          }
+          size += sprintf(BLS, "CHAN#%c (TGT %d LUN %d):\n",
+              'A' + channel, target, lun);
+          size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n",
+              sp->xfers, sp->r_total, sp->w_total);
+          size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n",
+              sp->r_total512, sp->w_total512);
+          size += sprintf(BLS, "%s\n", HDRB);
+          size += sprintf(BLS, " Reads:");
+          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->r_bins[0],
+              sp->r_bins[1], sp->r_bins[2], sp->r_bins[3]);
+          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->r_bins[4],
+              sp->r_bins[5], sp->r_bins[6], sp->r_bins[7]);
+          size += sprintf(BLS, "%6ld %6ld\n", sp->r_bins[8],
+              sp->r_bins[9]);
+          size += sprintf(BLS, "Writes:");
+          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->w_bins[0],
+              sp->w_bins[1], sp->w_bins[2], sp->w_bins[3]);
+          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->w_bins[4],
+              sp->w_bins[5], sp->w_bins[6], sp->w_bins[7]);
+          size += sprintf(BLS, "%6ld %6ld\n", sp->w_bins[8],
+              sp->w_bins[9]);
+          size += sprintf(BLS, "\n");
+        }
+      }
+    }
+    len += size; pos = begin + len; size = 0;
+  }
+#endif /* AIC7XXX_PROC_STATS */
+
+stop_output:
+  proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
+  *start = buffer + (offset - begin);   /* Start of wanted data */
+  len -= (offset - begin);      /* Start slop */
+  if (len > length)
+  {
+    len = length;               /* Ending slop */
+  }
+  proc_debug("3pos: %ld offset: %ld len: %d\n", pos, offset, len);
+  
+  return (len);     
+}
+
+/*
+ * 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: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/buslogic.c linux/drivers/scsi/buslogic.c
--- v1.3.35/linux/drivers/scsi/buslogic.c	Mon Sep 18 14:53:56 1995
+++ linux/drivers/scsi/buslogic.c	Sat Oct 21 19:43:31 1995
@@ -94,7 +94,7 @@
 #include <asm/system.h>
 #include <asm/dma.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/constants.c linux/drivers/scsi/constants.c
--- v1.3.35/linux/drivers/scsi/constants.c	Mon Sep 18 14:53:56 1995
+++ linux/drivers/scsi/constants.c	Sat Oct 21 19:43:31 1995
@@ -18,7 +18,7 @@
 #endif
 
 #include <linux/config.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include <linux/kernel.h>
 #include "scsi.h"
 #include "hosts.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v1.3.35/linux/drivers/scsi/eata.c	Mon Sep 18 14:53:56 1995
+++ linux/drivers/scsi/eata.c	Sat Oct 21 19:43:31 1995
@@ -122,7 +122,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <linux/proc_fs.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v1.3.35/linux/drivers/scsi/eata_dma.c	Wed Oct  4 14:14:32 1995
+++ linux/drivers/scsi/eata_dma.c	Sat Oct 21 19:43:31 1995
@@ -71,7 +71,7 @@
 #include <asm/types.h>
 #include <asm/io.h>
 #include <asm/dma.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "sd.h"
 #include "hosts.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/eata_pio.h linux/drivers/scsi/eata_pio.h
--- v1.3.35/linux/drivers/scsi/eata_pio.h	Mon Sep 18 14:53:56 1995
+++ linux/drivers/scsi/eata_pio.h	Sat Oct 21 19:51:03 1995
@@ -9,7 +9,7 @@
 #ifndef _EATA_PIO_H
 #define _EATA_PIO_H
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include <linux/scsicam.h>
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v1.3.35/linux/drivers/scsi/fdomain.c	Fri Oct 13 14:44:33 1995
+++ linux/drivers/scsi/fdomain.c	Sat Oct 21 19:43:31 1995
@@ -198,7 +198,7 @@
 
 #include <linux/sched.h>
 #include <asm/io.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "fdomain.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c
--- v1.3.35/linux/drivers/scsi/g_NCR5380.c	Mon Sep 18 14:53:57 1995
+++ linux/drivers/scsi/g_NCR5380.c	Sat Oct 21 19:43:31 1995
@@ -61,7 +61,7 @@
 #include <asm/io.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "g_NCR5380.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.35/linux/drivers/scsi/hosts.c	Wed Sep 27 15:59:59 1995
+++ linux/drivers/scsi/hosts.c	Sat Oct 21 19:43:31 1995
@@ -31,7 +31,7 @@
 #endif
 
 #include <linux/config.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- v1.3.35/linux/drivers/scsi/in2000.c	Mon Sep 18 14:53:57 1995
+++ linux/drivers/scsi/in2000.c	Sat Oct 21 19:43:31 1995
@@ -67,7 +67,7 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/io.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/pas16.c linux/drivers/scsi/pas16.c
--- v1.3.35/linux/drivers/scsi/pas16.c	Mon Sep 18 14:54:01 1995
+++ linux/drivers/scsi/pas16.c	Sat Oct 21 19:43:31 1995
@@ -111,7 +111,7 @@
 #include <linux/proc_fs.h>
 #include <linux/sched.h>
 #include <asm/io.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "pas16.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/qlogic.c linux/drivers/scsi/qlogic.c
--- v1.3.35/linux/drivers/scsi/qlogic.c	Mon Sep 25 12:26:22 1995
+++ linux/drivers/scsi/qlogic.c	Sat Oct 21 19:43:31 1995
@@ -119,7 +119,7 @@
 #undef MODULE
 #endif 
 
-#include "../block/blk.h"	/* to get disk capacity */
+#include <linux/blk.h>	/* to get disk capacity */
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.35/linux/drivers/scsi/scsi.c	Mon Oct 16 18:38:23 1995
+++ linux/drivers/scsi/scsi.c	Sat Oct 21 19:43:31 1995
@@ -45,7 +45,7 @@
 #include <linux/kernel.h>
 #include<linux/stat.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "constants.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c
--- v1.3.35/linux/drivers/scsi/scsi_debug.c	Mon Sep 18 14:54:02 1995
+++ linux/drivers/scsi/scsi_debug.c	Sat Oct 21 19:43:31 1995
@@ -30,7 +30,7 @@
 #include <linux/module.h>
 #endif
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v1.3.35/linux/drivers/scsi/scsi_ioctl.c	Mon Oct 16 18:38:24 1995
+++ linux/drivers/scsi/scsi_ioctl.c	Sat Oct 21 19:43:31 1995
@@ -22,7 +22,7 @@
 #include <linux/mm.h>
 #include <linux/string.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "scsi_ioctl.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v1.3.35/linux/drivers/scsi/scsi_proc.c	Wed Oct  4 14:14:32 1995
+++ linux/drivers/scsi/scsi_proc.c	Sat Oct 21 19:43:31 1995
@@ -33,7 +33,7 @@
 #include <linux/proc_fs.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v1.3.35/linux/drivers/scsi/scsi_syms.c	Mon Sep 18 14:54:02 1995
+++ linux/drivers/scsi/scsi_syms.c	Sat Oct 21 19:43:31 1995
@@ -29,7 +29,7 @@
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "scsi_ioctl.h"
 #include "hosts.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c
--- v1.3.35/linux/drivers/scsi/scsicam.c	Mon Sep 18 14:54:02 1995
+++ linux/drivers/scsi/scsicam.c	Sat Oct 21 19:43:31 1995
@@ -27,7 +27,7 @@
 #include <linux/fs.h>
 #include <linux/genhd.h>
 #include <linux/kernel.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.3.35/linux/drivers/scsi/sd.c	Mon Oct 16 18:38:24 1995
+++ linux/drivers/scsi/sd.c	Sat Oct 21 19:43:31 1995
@@ -39,7 +39,7 @@
 #include <asm/system.h>
 
 #define MAJOR_NR SCSI_DISK_MAJOR
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/sd_ioctl.c linux/drivers/scsi/sd_ioctl.c
--- v1.3.35/linux/drivers/scsi/sd_ioctl.c	Mon Sep 18 14:54:03 1995
+++ linux/drivers/scsi/sd_ioctl.c	Sat Oct 21 19:43:31 1995
@@ -13,7 +13,7 @@
 
 #include <asm/segment.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "scsi_ioctl.h"
 #include "hosts.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v1.3.35/linux/drivers/scsi/seagate.c	Mon Sep 18 14:54:03 1995
+++ linux/drivers/scsi/seagate.c	Sat Oct 21 19:43:31 1995
@@ -58,7 +58,7 @@
 #include <linux/config.h>
 #include <linux/proc_fs.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "seagate.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v1.3.35/linux/drivers/scsi/sg.c	Mon Sep 18 14:54:03 1995
+++ linux/drivers/scsi/sg.c	Sat Oct 21 19:43:31 1995
@@ -25,7 +25,7 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "scsi_ioctl.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.3.35/linux/drivers/scsi/sr.c	Mon Oct 16 18:38:24 1995
+++ linux/drivers/scsi/sr.c	Sat Oct 21 19:43:31 1995
@@ -35,7 +35,7 @@
 #include <asm/system.h>
 
 #define MAJOR_NR SCSI_CDROM_MAJOR
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sr.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v1.3.35/linux/drivers/scsi/sr_ioctl.c	Mon Oct 16 18:38:24 1995
+++ linux/drivers/scsi/sr_ioctl.c	Sat Oct 21 19:43:31 1995
@@ -11,7 +11,7 @@
 #include <asm/segment.h>
 #include <linux/errno.h>
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sr.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v1.3.35/linux/drivers/scsi/st.c	Wed Oct  4 14:14:32 1995
+++ linux/drivers/scsi/st.c	Sat Oct 21 19:43:31 1995
@@ -11,7 +11,7 @@
   Copyright 1992, 1993, 1994, 1995 Kai Makisara
 		 email Kai.Makisara@metla.fi
 
-  Last modified: Sat Sep 30 15:54:57 1995 by root@kai.makisara.fi
+  Last modified: Tue Oct 17 21:46:26 1995 by root@kai.makisara.fi
   Some small formal changes - aeb, 950809
 */
 #ifdef MODULE
@@ -38,7 +38,7 @@
 #define DEBUG 0
 
 #define MAJOR_NR SCSI_TAPE_MAJOR
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "scsi_ioctl.h"
@@ -123,6 +123,8 @@
 	   SCpnt->request_bufflen);
     if (driver_byte(result) & DRIVER_SENSE)
       print_sense("st", SCpnt);
+    else
+      printk("\n");
   }
 #endif
   scode = sense[2] & 0x0f;
@@ -132,12 +134,15 @@
        scode != RECOVERED_ERROR &&
        scode != UNIT_ATTENTION &&
        scode != BLANK_CHECK &&
-       scode != VOLUME_OVERFLOW)) {  /* Abnormal conditions for tape */
+       scode != VOLUME_OVERFLOW &&
+       SCpnt->data_cmnd[0] != MODE_SENSE)) {  /* Abnormal conditions for tape */
     printk("st%d: Error %x. ", dev, result);
+#if !DEBUG
     if (driver_byte(result) & DRIVER_SENSE)
       print_sense("st", SCpnt);
     else
       printk("\n");
+#endif
   }
 
   if ((sense[0] & 0x70) == 0x70 &&
@@ -202,9 +207,12 @@
     }
     else
       SCpnt->request.rq_status = RQ_SCSI_DONE;
-    if (!(STp->buffer)->writing || STp->write_pending)
-      wake_up( &(STp->waiting) );
+
+#if DEBUG
     STp->write_pending = 0;
+#endif
+    up(SCpnt->request.sem);
+    SCpnt->request.sem = NULL;
   }
 #if DEBUG
   else if (debugging)
@@ -218,8 +226,6 @@
 st_do_scsi(Scsi_Cmnd *SCpnt, Scsi_Tape *STp, unsigned char *cmd, int bytes,
 	   int timeout, int retries)
 {
-  unsigned int flags;
-
   if (SCpnt == NULL)
     if ((SCpnt = allocate_device(NULL, STp->device, 1)) == NULL) {
       printk("st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt));
@@ -227,18 +233,15 @@
     }
 
   cmd[1] |= (SCpnt->lun << 5) & 0xe0;
+  STp->sem = MUTEX_LOCKED;
+  SCpnt->request.sem = &(STp->sem);
   SCpnt->request.rq_status = RQ_SCSI_BUSY;
   SCpnt->request.rq_dev = STp->devt;
 
   scsi_do_cmd(SCpnt, (void *)cmd, (STp->buffer)->b_data, bytes,
 	      st_sleep_done, timeout, retries);
 
-  /* this must be done with interrupts off */
-  save_flags (flags);
-  cli();
-  if (SCpnt->request.rq_status != RQ_SCSI_DONE)
-    sleep_on( &(STp->waiting) );
-  restore_flags(flags);
+  down(SCpnt->request.sem);
 
   (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
 
@@ -251,24 +254,17 @@
 write_behind_check(Scsi_Tape *STp)
 {
   ST_buffer * STbuffer;
-  unsigned long flags;
 
   STbuffer = STp->buffer;
 
-  save_flags(flags);
-  cli();
-  if (STp->write_pending) {
 #if DEBUG
+  if (STp->write_pending)
     STp->nbr_waits++;
-#endif
-    sleep_on( &(STp->waiting) );
-    STp->write_pending = 0;
-  }
-#if DEBUG
   else
     STp->nbr_finished++;
 #endif
-  restore_flags(flags);
+
+  down(&(STp->sem));
 
   if (STbuffer->writing < STbuffer->buffer_bytes)
     memcpy(STbuffer->b_data,
@@ -483,7 +479,6 @@
     STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
 
     STp->dirty = 0;
-    STp->write_pending = 0;
     STp->rw = ST_IDLE;
     STp->ready = ST_READY;
     if (STp->eof != ST_EOD)  /* Save EOD across opens */
@@ -574,6 +569,7 @@
 #endif
       STp->block_size = ST_DEFAULT_BLOCK;  /* Educated guess (?) */
       (STp->buffer)->last_result_fatal = 0;  /* Prevent error propagation */
+      STp->drv_write_prot = 0;
     }
     else {
 
@@ -606,6 +602,7 @@
 	STp->in_use = 0;
 	return (-EIO);
       }
+      STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
     }
     SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
 
@@ -622,7 +619,6 @@
 	     (STp->buffer)->buffer_blocks);
 #endif
 
-    STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
     if (STp->drv_write_prot) {
       STp->write_prot = 1;
 #if DEBUG
@@ -965,9 +961,14 @@
       cmd[2] = blks >> 16;
       cmd[3] = blks >> 8;
       cmd[4] = blks;
+      STp->sem = MUTEX_LOCKED;
+      SCpnt->request.sem = &(STp->sem);
       SCpnt->request.rq_status = RQ_SCSI_BUSY;
       SCpnt->request.rq_dev = STp->devt;
+#if DEBUG
       STp->write_pending = 1;
+#endif
+
       scsi_do_cmd (SCpnt,
 		   (void *) cmd, (STp->buffer)->b_data,
 		   (STp->buffer)->writing,
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v1.3.35/linux/drivers/scsi/st.h	Wed Sep 27 15:59:59 1995
+++ linux/drivers/scsi/st.h	Wed Oct 18 10:34:20 1995
@@ -29,7 +29,6 @@
   struct wait_queue * waiting;
   Scsi_Device* device;
   unsigned char dirty;
-  unsigned char write_pending;
   unsigned char rw;
   unsigned char ready;
   unsigned char eof;
@@ -47,6 +46,7 @@
   unsigned char density;
   unsigned char door_locked;
   ST_buffer * buffer;
+  struct semaphore sem;
   int block_size;
   int min_block;
   int max_block;
@@ -58,6 +58,7 @@
   struct mtget * mt_status;
   Scsi_Cmnd SCpnt;
 #if DEBUG
+  unsigned char write_pending;
   int nbr_finished;
   int nbr_waits;
 #endif
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/t128.c linux/drivers/scsi/t128.c
--- v1.3.35/linux/drivers/scsi/t128.c	Mon Sep 18 14:54:04 1995
+++ linux/drivers/scsi/t128.c	Sat Oct 21 19:43:31 1995
@@ -109,7 +109,7 @@
 #include <asm/system.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "t128.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v1.3.35/linux/drivers/scsi/u14-34f.c	Mon Sep 18 14:54:04 1995
+++ linux/drivers/scsi/u14-34f.c	Sat Oct 21 19:43:31 1995
@@ -148,7 +148,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <linux/proc_fs.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c
--- v1.3.35/linux/drivers/scsi/ultrastor.c	Mon Sep 18 14:54:04 1995
+++ linux/drivers/scsi/ultrastor.c	Sat Oct 21 19:43:31 1995
@@ -140,7 +140,7 @@
 #include <asm/dma.h>
 
 #define ULTRASTOR_PRIVATE	/* Get the private stuff from ultrastor.h */
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "ultrastor.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v1.3.35/linux/drivers/scsi/wd7000.c	Mon Sep 18 14:54:04 1995
+++ linux/drivers/scsi/wd7000.c	Sat Oct 21 19:43:31 1995
@@ -116,7 +116,7 @@
 #include <asm/io.h>
 #include <linux/ioport.h>
 #include <linux/proc_fs.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/.objects linux/drivers/sound/.objects
--- v1.3.35/linux/drivers/sound/.objects	Thu Jan  1 02:00:00 1970
+++ linux/drivers/sound/.objects	Wed Oct 18 05:35:47 1995
@@ -0,0 +1,101 @@
+OBJS=
+
+ifdef CONFIG_AD1848
+	OBJS := $(OBJS)  ad1848.o
+endif
+
+ifdef CONFIG_YM3812
+	OBJS := $(OBJS)  adlib_card.o
+endif
+
+ifdef CONFIG_AEDSP16
+	OBJS := $(OBJS)  aedsp16.o
+endif
+
+ifdef CONFIG_AUDIO
+	OBJS := $(OBJS)  audio.o
+endif
+
+ifdef CONFIG_CS4232
+	OBJS := $(OBJS)  cs4232.o
+endif
+
+ifdef CONFIG_AUDIO
+	OBJS := $(OBJS)  dmabuf.o
+endif
+
+ifdef CONFIG_GUS
+	OBJS := $(OBJS)  gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
+endif
+
+ifdef CONFIG_MAD16
+	OBJS := $(OBJS)  mad16.o mad16_sb_midi.o
+endif
+
+ifdef CONFIG_MAUI
+	OBJS := $(OBJS)  maui.o
+endif
+
+ifdef CONFIG_MIDI
+	OBJS := $(OBJS)  midi_synth.o midibuf.o
+endif
+
+ifdef CONFIG_MPU401
+	OBJS := $(OBJS)  mpu401.o
+else
+  ifdef CONFIG_MPU_EMU
+	OBJS := $(OBJS)  mpu401.o
+  endif
+endif
+
+ifdef CONFIG_YM3812
+	OBJS := $(OBJS)  opl3.o
+endif
+
+ifdef CONFIG_PAS
+	OBJS := $(OBJS)  pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
+endif
+
+ifdef CONFIG_SEQUENCER
+	OBJS := $(OBJS)  patmgr.o
+endif
+
+ifdef CONFIG_PSS
+	OBJS := $(OBJS)  pss.o
+endif
+
+ifdef CONFIG_SB16
+	OBJS := $(OBJS)  sb16_dsp.o
+endif
+
+ifdef CONFIG_SB
+	OBJS := $(OBJS)  sb16_midi.o sb_card.o sb_dsp.o sb_midi.o sb_mixer.o
+	OBJS := $(OBJS)  sb_card.o
+	OBJS := $(OBJS)  sb_dsp.o
+	OBJS := $(OBJS)  sb_midi.o
+	OBJS := $(OBJS)  sb_mixer.o
+endif
+
+ifdef CONFIG_SEQUENCER
+	OBJS := $(OBJS)  sequencer.o
+endif
+
+ifdef CONFIG_SEQUENCER
+	OBJS := $(OBJS)  sound_timer.o
+endif
+
+ifdef CONFIG_SSCAPE
+	OBJS := $(OBJS)  sscape.o
+endif
+
+ifdef CONFIG_SEQUENCER
+	OBJS := $(OBJS)  sys_timer.o
+endif
+
+ifdef CONFIG_TRIX
+	OBJS := $(OBJS)  trix.o
+endif
+
+ifdef CONFIG_UART6850
+	OBJS := $(OBJS)  uart6850.o
+endif
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/.version linux/drivers/sound/.version
--- v1.3.35/linux/drivers/sound/.version	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/.version	Wed Oct 18 05:33:24 1995
@@ -1 +1 @@
-3.5-alpha2
+3.5-alpha3
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG
--- v1.3.35/linux/drivers/sound/CHANGELOG	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/CHANGELOG	Wed Oct 18 05:33:23 1995
@@ -1,5 +1,17 @@
-Changelog for version 3.5
--------------------------
+Changelog for version 3.5-alpha3
+--------------------------------
+
+Since 3.5-alpha2
+- Modifications to makefile and configure.c. Unnecessary sources
+  are no longer compiled. Newly created local.h is also copied to
+  /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces
+  new local.h which is compatible with current version of the driver.
+- Some fixes to the SB16 support.
+- Fixed random protection fault in gus_wave.c
+
+Since 3.5-alpha1
+- Modified to work with Linux-1.3.33 and leater
+- Some minor changes
 
 Since 3.0.2
 - Support for CS4232 based PnP cards (AcerMagic S23 etc).
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v1.3.35/linux/drivers/sound/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/drivers/sound/Config.in	Sat Oct 21 18:57:03 1995
@@ -0,0 +1,8 @@
+#
+# Sound driver configuration
+#
+# This is really ugly: it should probably be changed
+# to use the normal config script setup
+#
+$MAKE -C drivers/sound config || exit 1 ;;
+
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v1.3.35/linux/drivers/sound/Makefile	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/Makefile	Sun Oct 22 21:41:33 1995
@@ -9,14 +9,20 @@
 TARGET_OS	= linux
 USRINCDIR	= /usr/include
 MODULEDIR	= /lib/modules/misc
+FIXEDOBJS	= soundcard.o dev_table.o sound_switch.o
 
-OBJS   = soundcard.o audio.o dmabuf.o sb_dsp.o dev_table.o \
+ifeq (.defines,$(wildcard .defines))
+include .defines
+include .objects
+else
+OBJS   = audio.o dmabuf.o sb_dsp.o \
 	 opl3.o sequencer.o midibuf.o sb_card.o pas2_card.o adlib_card.o \
 	 pas2_pcm.o pas2_mixer.o pas2_midi.o gus_card.o gus_wave.o mpu401.o \
 	 gus_midi.o gus_vol.o patmgr.o sb_mixer.o sb16_dsp.o sb_midi.o \
-	 sb16_midi.o sound_switch.o midi_synth.o uart6850.o sound_timer.o \
+	 sb16_midi.o midi_synth.o uart6850.o sound_timer.o \
 	 sys_timer.o ics2101.o ad1848.o pss.o sscape.o trix.o aedsp16.o \
-	 mad16.o mad16_sb_midi.o cs4232.o maui.o sound_pnp.o
+	 mad16.o mad16_sb_midi.o cs4232.o maui.o
+endif
 
 ifndef HOSTCC
 #
@@ -38,8 +44,15 @@
 	$(CC) $(CFLAGS) -c $<
 endif
 
+ifeq ($(CONFIG_SOUND),y)
+OBJS += $(FIXEDOBJS)
+
 all:	local.h sound.a
 
+else
+all:
+endif
+
 include $(TOPDIR)/Rules.make
 
 sound.a: $(OBJS) 
@@ -57,8 +70,10 @@
 local.h: 
 	$(MAKE) clean
 	$(MAKE) setup-$(TARGET_OS)
-	$(MAKE) config
+	$(MAKE) oldconfig
 	$(MAKE) dep
+	@echo You have to restart make. This ensures that options are read corrrectly.
+	exit -1
 
 config: configure
 	@$(MAKE) setup-$(TARGET_OS)
@@ -69,8 +84,16 @@
 #	@echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
 	@echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
 
+oldconfig: setup-$(TARGET_OS) configure
+	@./configure /etc/soundconf > local.h
+	@echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
+	@echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
+#	@echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
+#	@echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
+	@echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
+
 clrconf:
-	rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h
+	rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h .defines
 
 configure: configure.c
 	$(HOSTCC) -o configure configure.c
@@ -82,9 +105,9 @@
 setup-linux:
 	@echo Compiling Sound Driver v $(VERSION) for Linux
 
-sound.o: $(OBJS) 
+sound.o: local.h $(FIXEDOBJS) sound.a
 	-rm -f sound.o
-	$(LD) -r -o sound.o $(OBJS)
+	$(LD) -r -o sound.o $(FIXEDOBJS) sound.a
 
 modules: local.h sound.o
 	ln -fs `pwd`/sound.o /usr/src/linux/modules/sound.o
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v1.3.35/linux/drivers/sound/Readme	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/Readme	Wed Oct 18 05:33:23 1995
@@ -1,3 +1,7 @@
+
+VoxWare v3.5-alpha3 release notes
+---------------------------------
+
 IMPORTANT! This version of the driver is compatible only with Linux versions
 	   1.3.33 and later. It may work with earlier ones as a loadable
 	   module but...
@@ -5,7 +9,8 @@
 	   Also this is an ALPHA test version which has not been tested
 	   with all cards. At least AEDSP16 support will not work. PAS16
 	   and PSS supports have not been tested. /dev/dsp and /dev/audio
- 	   playback with standard GUS sounds scrambled.
+ 	   playback with standard GUS sounds scrambled. 16 bit mode of
+	   SB16 doesn't work.
 
 Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux ftp 
 sites). It contains much more information than this file.
@@ -16,13 +21,6 @@
 * about configuring various cards.				*
 *****************************************************************
 
-VoxWare v3.5-alpha2 release notes
----------------------------------
-
-This is not an official release but just an experimental alpha
-test version. The official version is included in Linux-1.3.3x
-version sources.
-
 There are some programming information (little bit old) in the
 Hacker's Guide 
 (ftp://nic.funet.fi/pub/OS/Linux/ALPHA/sound/snd-sdk-doc-0.1.ps.gz).
@@ -35,26 +33,13 @@
 after v3.0 (I will not add aditional features before v3.0 is ready).
 
    ====================================================
--  THIS VERSION ____REQUIRES____ Linux 1.2.0 OR LATER.
+-  THIS VERSION ____REQUIRES____ Linux 1.3.33 OR LATER.
    ====================================================
 
 -  THIS VERSION MAY NOT WORK WITH Linux VERSIONS RELEASED
-   AFTER end of July 1995. If this version doesn't compile with
+   AFTER end of Nov 1995. If this version doesn't compile with
    your kernel version, please use the sound driver version
    included in your kernel.
--  Ensoniq SoundScape support is included in this version but it's
-   somehow incomplete. MIDI recording may not work and you have to
-   use /dev/dsp1 and /dev/audio1 (/dev/dsp and /dev/audio don't work).
-   I recommend you recreate the device files used by the driver by
-   running the script at the end of linux/Readme. Then just
-   rm /dev/dsp;ln -s /dev/dsp1 /dev/dsp (and the same with /dev/audio).
-   You will also need the latest version of the soundscape.co[01] file.
-   The old sndscape.cod will ___NOT___ work (it propably just hangs your
-   system completely). The latest code file is available from ftp.ensoniq.com
-   as part of the DOS/Windows driver distribution.
--  This is Linux only version. It should work in other operating systems
-   (SCO, UW, FreeBSD and NetBSD) too but may require some fixes before
-   it compiles.
 
 You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z
 packages to use this driver. They should be in the same
@@ -86,6 +71,8 @@
 (NOTE! The MAD16 looks similar to the Mozart chip. It could be a good
 idea to configure MAD16 cards as Mozart ones. The MAD16 driver doesn't set
 up MPU401 which the Mozart one does.
+CS4232 based cards such as AcerMagic S23.
+
 
 In addition all Sound Blaster models and clones (up to AWE32) work if
 you want to use them.
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/adlib_card.c linux/drivers/sound/adlib_card.c
--- v1.3.35/linux/drivers/sound/adlib_card.c	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/adlib_card.c	Wed Oct 18 05:33:24 1995
@@ -35,11 +35,9 @@
 attach_adlib_card (long mem_start, struct address_info *hw_config)
 {
 
-  if (opl3_detect (hw_config->io_base, hw_config->osp))
-    {
-      mem_start = opl3_init (mem_start, hw_config->io_base, hw_config->osp);
-      request_region (hw_config->io_base, 4, "OPL3/OPL2");
-    }
+  mem_start = opl3_init (mem_start, hw_config->io_base, hw_config->osp);
+  request_region (hw_config->io_base, 4, "OPL3/OPL2");
+
   return mem_start;
 }
 
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v1.3.35/linux/drivers/sound/audio.c	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/audio.c	Wed Oct 18 05:33:24 1995
@@ -154,20 +154,6 @@
   DMAbuf_release (dev, mode);
 }
 
-#ifdef NO_INLINE_ASM
-static void
-translate_bytes (const unsigned char *table, unsigned char *buff, int n)
-{
-  unsigned long   i;
-
-  if (n <= 0)
-    return;
-
-  for (i = 0; i < n; ++i)
-    buff[i] = table[buff[i]];
-}
-
-#else
 extern inline void
 translate_bytes (const void *table, void *buff, int n)
 {
@@ -183,7 +169,6 @@
     }
 }
 
-#endif
 
 int
 audio_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int count)
@@ -247,7 +232,7 @@
 	{			/*
 				 * No device specific copy routine
 				 */
-	  memcpy_fromfs ((&wr_dma_buf[dev][wr_buff_ptr[dev]]), &((buf)[p]), (l));
+	  memcpy_fromfs (&wr_dma_buf[dev][wr_buff_ptr[dev]], &((buf)[p]), l);
 	}
       else
 	audio_devs[dev]->copy_from_user (dev,
@@ -343,7 +328,7 @@
 	  translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l);
 	}
 
-      memcpy_tofs (&((buf)[p]), (dmabuf), (l));
+      memcpy_tofs (&((buf)[p]), dmabuf, l);
 
       DMAbuf_rmchars (dev, buff_no, l);
 
@@ -418,7 +403,7 @@
 	  if (err < 0)
 	    return err;
 
-	  memcpy_tofs (&(((char *) arg)[0]), ((char *) &info), (sizeof (info)));
+	  memcpy_tofs ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 
@@ -437,7 +422,7 @@
 	  if (wr_buff_no[dev] != -1)
 	    info.bytes += wr_buff_size[dev] - wr_buff_ptr[dev];
 
-	  memcpy_tofs (&(((char *) arg)[0]), ((char *) &info), (sizeof (info)));
+	  memcpy_tofs ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 
@@ -462,7 +447,7 @@
 	  if (audio_devs[dev]->trigger)		/* Supports SETTRIGGER */
 	    info |= DSP_CAP_TRIGGER;
 
-	  memcpy_tofs (&(((char *) arg)[0]), ((char *) &info), (sizeof (info)));
+	  memcpy_tofs ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 	break;
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v1.3.35/linux/drivers/sound/configure.c	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/configure.c	Wed Oct 18 05:33:24 1995
@@ -1,4 +1,4 @@
-#define DISABLED_OPTIONS 	0
+#define DISABLED_OPTIONS 	(B(OPT_PNP))
 /*
  * sound/configure.c  - Configuration program for the Linux Sound Driver
  *
@@ -53,18 +53,20 @@
 #define OPT_MAD16	12
 #define OPT_CS4232	13
 #define OPT_MAUI	14
+#define OPT_PNP		15
+
+#define OPT_HIGHLEVEL   16	/* This must be same than the next one */
+#define OPT_SBPRO	16
+#define OPT_SB16	17
+#define OPT_AEDSP16     18
+#define OPT_AUDIO	19
+#define OPT_MIDI_AUTO	20
+#define OPT_MIDI	21
+#define OPT_YM3812_AUTO	22
+#define OPT_YM3812	23
+#define OPT_SEQUENCER	24
+#define OPT_LAST	24	/* Last defined OPT number */
 
-#define OPT_HIGHLEVEL   15	/* This must be same than the next one */
-#define OPT_SBPRO	15
-#define OPT_SB16	16
-#define OPT_AEDSP16     17
-#define OPT_AUDIO	18
-#define OPT_MIDI_AUTO	19
-#define OPT_MIDI	20
-#define OPT_YM3812_AUTO	21
-#define OPT_YM3812	22
-#define OPT_SEQUENCER	23
-#define OPT_LAST	23	/* Last defined OPT number */
 
 #define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_SEQUENCER)|B(OPT_GUS)| \
 		  B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)| \
@@ -76,6 +78,11 @@
 #define MIDI_CARDS (B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | \
 		    B (OPT_GUS) | B (OPT_TRIX) | B (OPT_SSCAPE)|B(OPT_MAD16) | \
 		    B (OPT_CS4232)|B(OPT_MAUI))
+#define MPU_DEVS (B(OPT_PSS)|B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|\
+		  B(OPT_CS4232)|B(OPT_PNP)|B(OPT_MAUI))
+#define AD1848_DEVS (B(OPT_GUS16)|B(OPT_MSS)|B(OPT_PSS)|B(OPT_GUSMAX)|\
+		     B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\
+		     B(OPT_PNP))
 /*
  * Options that have been disabled for some reason (incompletely implemented
  * and/or tested). Don't remove from this list before looking at file
@@ -130,6 +137,7 @@
   {0, 0, "MAD16", 1, 0, 0},
   {0, 0, "CS4232", 1, 0, 0},
   {0, 0, "MAUI", 1, 0, 0},
+  {0, 0, "PNP", 1, 0, 0},
 
   {B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0, 1},
   {B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0, 1},
@@ -159,6 +167,7 @@
   "Support for MAD16 and/or Mozart based cards",
   "Support for Crystal CS4232 based (PnP) cards",
   "Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers",
+  "Support for PnP soundcards (_EXPERIMENTAL_)",
 
   "SoundBlaster Pro support",
   "SoundBlaster 16 support",
@@ -172,9 +181,33 @@
   "Is the sky really falling"
 };
 
+struct kludge
+  {
+    char           *name;
+    int             mask;
+  }
+extra_options[] =
+{
+  {
+    "MPU_EMU", MPU_DEVS
+  }
+  ,
+  {
+    "AD1848", AD1848_DEVS
+  }
+  ,
+  {
+    NULL, 0
+  }
+};
+
+int             old_config_used = 0;
+
 unsigned long   selected_options = 0;
 int             sb_dma = 0;
 
+void            build_defines (void);
+
 #include "hex2hex.h"
 int             bin2hex (char *path, char *target, char *varname);
 
@@ -259,6 +292,214 @@
   return num;
 }
 
+void
+rebuild_file (char *line)
+{
+  char           *method, *new, *old, *var, *p;
+
+  method = p = line;
+
+  while (*p && *p != ' ')
+    p++;
+  *p++ = 0;
+
+  old = p;
+  while (*p && *p != ' ')
+    p++;
+  *p++ = 0;
+
+  new = p;
+  while (*p && *p != ' ')
+    p++;
+  *p++ = 0;
+
+  var = p;
+  while (*p && *p != ' ')
+    p++;
+  *p++ = 0;
+
+  fprintf (stderr, "Rebuilding file %s (%s %s)\n", new, method, old);
+
+  if (strcmp (method, "bin2hex") == 0)
+    {
+      if (!bin2hex (old, new, var))
+	{
+	  fprintf (stderr, "Rebuild failed\n");
+	  exit (-1);
+	}
+    }
+  else if (strcmp (method, "hex2hex") == 0)
+    {
+      if (!hex2hex (old, new, var))
+	{
+	  fprintf (stderr, "Rebuild failed\n");
+	  exit (-1);
+	}
+    }
+  else
+    {
+      fprintf (stderr, "Failed to build '%s' - unknown method %s\n",
+	       new, method);
+      exit (-1);
+    }
+}
+
+int
+use_old_config (char *filename)
+{
+  char            buf[1024];
+  int             i = 0;
+
+  FILE           *oldf;
+
+  fprintf (stderr, "Copying old configuration from %s\n", filename);
+
+  if ((oldf = fopen (filename, "r")) == NULL)
+    {
+      fprintf (stderr, "Couldn't open previous configuration file\n");
+      perror (filename);
+      return 0;
+    }
+
+  while (fgets (buf, 1024, oldf) != NULL)
+    {
+      char            tmp[100];
+
+      if (buf[0] != '#')
+	{
+	  printf ("%s", buf);
+
+	  strncpy (tmp, buf, 8);
+	  tmp[8] = 0;
+
+	  if (strcmp (tmp, "/*build ") == 0)
+	    rebuild_file (&buf[8]);
+
+	  continue;
+	}
+
+      strncpy (tmp, buf, 8);
+      tmp[8] = 0;
+
+      if (strcmp (tmp, "#define ") == 0)
+	{
+	  char           *id = &buf[8];
+
+	  i = 0;
+	  while (id[i] && id[i] != ' ' &&
+		 id[i] != '\t' && id[i] != '\n')
+	    i++;
+
+	  strncpy (tmp, id, i);
+	  tmp[i] = 0;
+
+	  if (strcmp (tmp, "SELECTED_SOUND_OPTIONS") == 0)
+	    continue;
+
+	  tmp[8] = 0;		/* Truncate the string */
+	  if (strcmp (tmp, "EXCLUDE_") == 0)
+	    continue;		/* Skip excludes */
+
+	  printf ("%s", buf);
+	  continue;
+	}
+
+      if (strcmp (tmp, "#undef  ") == 0)
+	{
+	  char           *id = &buf[8];
+
+	  i = 0;
+	  while (id[i] && id[i] != ' ' &&
+		 id[i] != '\t' && id[i] != '\n')
+	    i++;
+
+	  strncpy (tmp, id, i);
+	  tmp[i] = 0;
+
+	  tmp[8] = 0;		/* Truncate the string */
+	  if (strcmp (tmp, "EXCLUDE_") != 0)
+	    continue;		/* Not a #undef  EXCLUDE_ line */
+	  strncpy (tmp, &id[8], i - 8);
+	  tmp[i - 8] = 0;
+
+	  for (i = 0; i <= OPT_LAST; i++)
+	    if (strcmp (hw_table[i].macro, tmp) == 0)
+	      {
+		selected_options |= (1 << i);
+		break;
+	      }
+	  continue;
+	}
+
+      printf ("%s", buf);
+    }
+  fclose (oldf);
+
+  for (i = 0; i <= OPT_LAST; i++)
+    if (!hw_table[i].alias)
+      if (selected_options & B (i))
+	printf ("#undef  EXCLUDE_%s\n", hw_table[i].macro);
+      else
+	printf ("#define EXCLUDE_%s\n", hw_table[i].macro);
+
+
+  printf ("\n");
+
+  i = 0;
+
+  while (extra_options[i].name != NULL)
+    {
+      if (selected_options & extra_options[i].mask)
+	printf ("#undef  EXCLUDE_%s\n", extra_options[i].name);
+      else
+	printf ("#define EXCLUDE_%s\n", extra_options[i].name);
+      i++;
+    }
+
+  printf ("\n");
+
+  printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
+  fprintf (stderr, "Old configuration copied.\n");
+
+  build_defines ();
+  old_config_used = 1;
+  return 1;
+}
+
+void
+build_defines (void)
+{
+  FILE           *optf;
+  int             i;
+
+  if ((optf = fopen (".defines", "w")) == NULL)
+    {
+      perror (".defines");
+      exit (-1);
+    }
+
+
+  for (i = 0; i <= OPT_LAST; i++)
+    if (!hw_table[i].alias)
+      if (selected_options & B (i))
+	fprintf (optf, "CONFIG_%s=y\n", hw_table[i].macro);
+
+
+  fprintf (optf, "\n");
+
+  i = 0;
+
+  while (extra_options[i].name != NULL)
+    {
+      if (selected_options & extra_options[i].mask)
+	fprintf (optf, "CONFIG_%s=y\n", extra_options[i].name);
+      i++;
+    }
+
+  fprintf (optf, "\n");
+  fclose (optf);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -266,9 +507,24 @@
   char            answ[10];
   int             sb_base = 0;
 
+  fprintf (stderr, "\nConfiguring the sound support\n\n");
+
+  if (argc > 1)
+    {
+      if (use_old_config (argv[1]))
+	exit (0);
+    }
+  else if (access ("/etc/soundconf", R_OK) == 0)
+    {
+      fprintf (stderr, "Old configuration exists in /etc/soundconf. Use it (y/n) ? ");
+      if (think_positively (0))
+	if (use_old_config ("/etc/soundconf"))
+	  exit (0);
+
+    }
+
   printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n\n");
 
-  fprintf (stderr, "\nConfiguring the sound support\n\n");
   {
     /*
      * Partial driver
@@ -360,7 +616,10 @@
 			goto midi0001_again;
 		    }
 		  else
-		    printf ("#define SMW_MIDI0001_INCLUDED\n");
+		    {
+		      printf ("#define SMW_MIDI0001_INCLUDED\n");
+		      printf ("/*build bin2hex %s smw-midi0001.h smw_ucode */\n", path);
+		    }
 		}
 	    }
 	}
@@ -446,7 +705,10 @@
 		goto genld_again;
 	    }
 	  else
-	    printf ("#define PSS_HAVE_LD\n");
+	    {
+	      printf ("#define PSS_HAVE_LD\n");
+	      printf ("/*build bin2hex %s synth-ld.h pss_synth */\n", path);
+	    }
 	}
       else
 	{
@@ -481,8 +743,9 @@
 	  scanf ("%s", path);
 	  fprintf (stderr, "including HEX file %s\n", path);
 
-	  if (!hex2hex (path, "trix_boot.h", "static unsigned char trix_boot"))
+	  if (!hex2hex (path, "trix_boot.h", "trix_boot"))
 	    goto hex2hex_again;
+	  printf ("/*build hex2hex %s trix_boot.h trix_boot */\n", path);
 	  printf ("#define INCLUDE_TRIX_BOOT\n");
 	}
     }
@@ -497,8 +760,6 @@
   else
     printf ("#define KERNEL_SOUNDCARD\n");
 
-  printf ("#define EXCLUDE_PNP\n");
-
   for (i = 0; i <= OPT_LAST; i++)
     if (!hw_table[i].alias)
       if (selected_options & B (i))
@@ -507,10 +768,27 @@
 	printf ("#define EXCLUDE_%s\n", hw_table[i].macro);
 
 
+  printf ("\n");
+
+  i = 0;
+
+  while (extra_options[i].name != NULL)
+    {
+      if (selected_options & extra_options[i].mask)
+	printf ("#undef  EXCLUDE_%s\n", extra_options[i].name);
+      else
+	printf ("#define EXCLUDE_%s\n", extra_options[i].name);
+      i++;
+    }
+
+  printf ("\n");
+
+
+
+  build_defines ();
   /*
    * IRQ and DMA settings
    */
-  printf ("\n");
 
   if (selected_options & B (OPT_AEDSP16))
     {
@@ -798,6 +1076,33 @@
       printf ("#define MPU_IRQ %d\n", num);
     }
 
+  if (selected_options & B (OPT_MAUI))
+    {
+      fprintf (stderr, "\nI/O base for TB Maui (MIDI I/O of TB Tropez)?\n"
+	       "The factory default is 330\n"
+	"Valid alternatives are 210, 230, 260, 290, 300, 320, 338 and 330\n"
+	       "Enter the Maui/Tropez MIDI I/O base: ");
+
+      num = ask_value ("%x", 0x330);
+      fprintf (stderr, "Maui I/O base set to %03x\n", num);
+      printf ("#define MAUI_BASE 0x%03x\n", num);
+
+      fprintf (stderr, "\nIRQ number for TB Maui (TB Tropez MIDI)?\n"
+	       "Valid numbers are: 5, 9, 12 and 15.\n"
+	       "The default value is 9.\n"
+	       "Enter the value: ");
+
+      num = ask_value ("%d", 9);
+      if (num == 6 || num < 3 || num > 15)
+	{
+
+	  fprintf (stderr, "*** Illegal input! ***\n");
+	  num = 5;
+	}
+      fprintf (stderr, "Maui/Tropez MIDI IRQ set to %d\n", num);
+      printf ("#define MAUI_IRQ %d\n", num);
+    }
+
   if (selected_options & B (OPT_UART6850))
     {
       fprintf (stderr, "\nI/O base for 6850 UART Midi?\n"
@@ -1394,6 +1699,16 @@
   fprintf (stderr, "Remember to update the System file\n");
 #endif
 
+  if (!old_config_used)
+    {
+      fprintf (stderr, "Save this configuration to /etc/soundconf (y/n)");
+      if (think_positively (1))
+	{
+	  fclose (stdout);
+	  if (system ("cp local.h /etc/soundconf") != 0)
+	    perror ("'cp local.h /etc/soundconf'");
+	}
+    }
   exit (0);
 }
 
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v1.3.35/linux/drivers/sound/dev_table.c	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/dev_table.c	Wed Oct 18 05:33:24 1995
@@ -72,13 +72,10 @@
 						 */
 	else if (sound_drivers[drv].probe (&snd_installed_cards[i].config))
 	  {
-#ifndef SHORT_BANNERS
 	    printk ("snd%d",
 		    snd_installed_cards[i].card_type);
-#endif
 
 	    mem_start = sound_drivers[drv].attach (mem_start, &snd_installed_cards[i].config);
-#ifndef SHORT_BANNERS
 	    printk (" at 0x%x irq %d drq %d",
 		    snd_installed_cards[i].config.io_base,
 		    snd_installed_cards[i].config.irq,
@@ -88,7 +85,6 @@
 		      snd_installed_cards[i].config.dma2);
 	    else
 	      printk ("\n");
-#endif
 	  }
 	else
 	  snd_installed_cards[i].enabled = 0;	/*
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v1.3.35/linux/drivers/sound/dev_table.h	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/dev_table.h	Sat Oct 21 12:20:45 1995
@@ -280,12 +280,8 @@
 	struct driver_info sound_drivers[] = {
 #ifndef EXCLUDE_PSS
 	  {"PSSECHO", SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss, unload_pss},
-#	ifdef PSS_MPU_BASE
 	  {"PSSMPU", SNDCARD_PSS_MPU, "PSS-MPU", attach_pss_mpu, probe_pss_mpu, unload_pss_mpu},
-#	endif
-#	ifdef PSS_MSS_BASE
 	  {"PSSMSS", SNDCARD_PSS_MSS, "PSS-MSS", attach_pss_mss, probe_pss_mss, unload_pss_mss},
-#	endif
 #endif
 #ifndef EXCLUDE_MSS
 		{"MSS", SNDCARD_MSS,	"MS Sound System",	attach_ms_sound, probe_ms_sound, unload_ms_sound},
@@ -356,7 +352,7 @@
 
 	struct card_info snd_installed_cards[] = {
 #ifndef EXCLUDE_PSS
-	     {SNDCARD_PSS, {PSS_BASE, PSS_IRQ, PSS_DMA, -1}, SND_DEFAULT_ENABLE},
+	     {SNDCARD_PSS, {PSS_BASE, 0, -1, -1}, SND_DEFAULT_ENABLE},
 #	ifdef PSS_MPU_BASE
 	     {SNDCARD_PSS_MPU, {PSS_MPU_BASE, PSS_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE},
 #	endif
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v1.3.35/linux/drivers/sound/dmabuf.c	Fri Oct 13 14:44:33 1995
+++ linux/drivers/sound/dmabuf.c	Wed Oct 18 05:33:24 1995
@@ -864,7 +864,7 @@
 	info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan2, audio_devs[dev]->dmap_in);
 	info.blocks = audio_devs[dev]->dmap_in->qlen;
 	info.bytes += info.ptr;
-	memcpy_tofs (&(((char *) arg)[0]), ((char *) &info), (sizeof (info)));
+	memcpy_tofs ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 
 	if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
 	  audio_devs[dev]->dmap_in->qlen = 0;	/* Acknowledge interrupts */
@@ -884,7 +884,7 @@
 	info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan1, audio_devs[dev]->dmap_out);
 	info.blocks = audio_devs[dev]->dmap_out->qlen;
 	info.bytes += info.ptr;
-	memcpy_tofs (&(((char *) arg)[0]), ((char *) &info), (sizeof (info)));
+	memcpy_tofs ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 
 	if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
 	  audio_devs[dev]->dmap_out->qlen = 0;	/* Acknowledge interrupts */
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v1.3.35/linux/drivers/sound/gus_wave.c	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/gus_wave.c	Wed Oct 18 05:33:25 1995
@@ -896,7 +896,7 @@
     {
     case SNDCTL_SYNTH_INFO:
       gus_info.nr_voices = nr_voices;
-      memcpy_tofs (&(((char *) arg)[0]), (&gus_info), (sizeof (gus_info)));
+      memcpy_tofs ((&((char *) arg)[0]), &gus_info, sizeof (gus_info));
       return 0;
       break;
 
@@ -1029,7 +1029,7 @@
 static void
 compute_and_set_volume (int voice, int volume, int ramp_time)
 {
-  int             current, target, rate;
+  int             curr, target, rate;
   unsigned long   flags;
 
   compute_volume (voice, volume);
@@ -1043,7 +1043,7 @@
 
   gus_select_voice (voice);
 
-  current = gus_read16 (0x09) >> 4;
+  curr = gus_read16 (0x09) >> 4;
   target = voices[voice].initial_volume;
 
   if (ramp_time == INSTANT_RAMP)
@@ -1060,7 +1060,7 @@
     rate = 16;
   gus_ramp_rate (0, rate);
 
-  if ((target - current) / 64 == 0)	/* Close enough to target. */
+  if ((target - curr) / 64 == 0)	/* Close enough to target. */
     {
       gus_rampoff ();
       gus_voice_volume (target);
@@ -1068,11 +1068,11 @@
       return;
     }
 
-  if (target > current)
+  if (target > curr)
     {
       if (target > (4095 - 65))
 	target = 4095 - 65;
-      gus_ramp_range (current, target);
+      gus_ramp_range (curr, target);
       gus_rampon (0x00);	/* Ramp up, once, no IRQ */
     }
   else
@@ -1080,7 +1080,7 @@
       if (target < 65)
 	target = 65;
 
-      gus_ramp_range (target, current);
+      gus_ramp_range (target, curr);
       gus_rampon (0x40);	/* Ramp down, once, no irq */
     }
   restore_flags (flags);
@@ -1330,7 +1330,9 @@
       init_envelope (voice);
     }
   else
-    compute_and_set_volume (voice, volume, 0);
+    {
+      compute_and_set_volume (voice, volume, 0);
+    }
 
   save_flags (flags);
   cli ();
@@ -1399,7 +1401,9 @@
   if (note_num == 255)
     {
       if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
-	voices[voice].volume_pending = volume;
+	{
+	  voices[voice].volume_pending = volume;
+	}
       else
 	{
 	  ret_val = guswave_start_note2 (dev, voice, note_num, volume);
@@ -1540,7 +1544,7 @@
    * been transferred already.
    */
 
-  memcpy_fromfs ((&((char *) &patch)[offs]), &((addr)[offs]), (sizeof_patch - offs));
+  memcpy_fromfs (&((char *) &patch)[offs], &((addr)[offs]), sizeof_patch - offs);
 
   instr = patch.instr_no;
 
@@ -1683,7 +1687,7 @@
 	   * OK, move now. First in and then out.
 	   */
 
-	  memcpy_fromfs ((audio_devs[gus_devnum]->dmap_out->raw_buf), &((addr)[sizeof_patch + src_offs]), (blk_size));
+	  memcpy_fromfs (audio_devs[gus_devnum]->dmap_out->raw_buf, &((addr)[sizeof_patch + src_offs]), blk_size);
 
 	  save_flags (flags);
 	  cli ();
@@ -2430,7 +2434,7 @@
 {
   if (gus_sampling_channels == 1)
     {
-      memcpy_fromfs ((&localbuf[localoffs]), &((userbuf)[useroffs]), (len));
+      memcpy_fromfs (&localbuf[localoffs], &((userbuf)[useroffs]), len);
     }
   else if (gus_sampling_bits == 8)
     {
@@ -3116,14 +3120,9 @@
     }
 
 
-  {
-    caddr_t         ptr;
-
-    ptr = sound_mem_blocks[sound_num_blocks] = kmalloc ((MAX_SAMPLE + 1) * sizeof (*samples), GFP_KERNEL);
-    if (sound_num_blocks < 1024)
-      sound_num_blocks++;
-    samples = (struct patch_info *) ptr;
-  };
+  samples = (struct patch_info *) (sound_mem_blocks[sound_num_blocks] = kmalloc ((MAX_SAMPLE + 1) * sizeof (*samples), GFP_KERNEL));
+  if (sound_num_blocks < 1024)
+    sound_num_blocks++;;
 
   reset_sample_memory ();
 
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/hex2hex.h linux/drivers/sound/hex2hex.h
--- v1.3.35/linux/drivers/sound/hex2hex.h	Tue Jul 11 10:02:53 1995
+++ linux/drivers/sound/hex2hex.h	Wed Oct 18 05:33:23 1995
@@ -81,7 +81,7 @@
 	}
 
 	fprintf(outf, "/*\n *\t Computer generated file. Do not edit.\n */\n");
-	fprintf(outf, "%s[] = {\n", varline);
+	fprintf(outf, "static unsigned char %s[] = {\n", varline);
 
 	for (i=0;i<l;i++)
 	{
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v1.3.35/linux/drivers/sound/maui.c	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/maui.c	Wed Oct 18 05:33:25 1995
@@ -85,7 +85,7 @@
 	}
     }
 
-  printk ("Maui: Transmit timeout\n");
+  printk ("Maui: Write timeout\n");
 
   return 0;
 }
@@ -128,7 +128,7 @@
    * been transferred already.
    */
 
-  memcpy_fromfs ((&((char *) &header)[offs]), &((addr)[offs]), (hdr_size - offs));
+  memcpy_fromfs (&((char *) &header)[offs], &((addr)[offs]), hdr_size - offs);
 
   if (count < header.len)
     {
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c
--- v1.3.35/linux/drivers/sound/midi_synth.c	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/midi_synth.c	Wed Oct 18 05:33:25 1995
@@ -284,7 +284,7 @@
     {
 
     case SNDCTL_SYNTH_INFO:
-      memcpy_tofs (&(((char *) arg)[0]), (synth_devs[dev]->info), (sizeof (struct synth_info)));
+      memcpy_tofs ((&((char *) arg)[0]), synth_devs[dev]->info, sizeof (struct synth_info));
 
       return 0;
       break;
@@ -526,7 +526,7 @@
    * been transferred already.
    */
 
-  memcpy_fromfs ((&((char *) &sysex)[offs]), &((addr)[offs]), (hdr_size - offs));
+  memcpy_fromfs (&((char *) &sysex)[offs], &((addr)[offs]), hdr_size - offs);
 
   if (count < sysex.len)
     {
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v1.3.35/linux/drivers/sound/midibuf.c	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/midibuf.c	Wed Oct 18 05:33:25 1995
@@ -246,14 +246,7 @@
     input_sleep_flag[dev].mode = WK_NONE;
   };
 
-  midi_in_buf[dev] = (struct midi_buf *) (
-					   {
-					   caddr_t x;
-			 x = kmalloc (sizeof (struct midi_buf), GFP_KERNEL);
-
-					   x;
-					   }
-  );
+  midi_in_buf[dev] = (struct midi_buf *) kmalloc (sizeof (struct midi_buf), GFP_KERNEL);
 
   if (midi_in_buf[dev] == NULL)
     {
@@ -264,14 +257,7 @@
     }
   midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
 
-  midi_out_buf[dev] = (struct midi_buf *) (
-					    {
-					    caddr_t x;
-			 x = kmalloc (sizeof (struct midi_buf), GFP_KERNEL);
-
-					    x;
-					    }
-  );
+  midi_out_buf[dev] = (struct midi_buf *) kmalloc (sizeof (struct midi_buf), GFP_KERNEL);
 
   if (midi_out_buf[dev] == NULL)
     {
@@ -417,7 +403,7 @@
 
       for (i = 0; i < n; i++)
 	{
-	  memcpy_fromfs ((&tmp_data), &((buf)[c]), (1));
+	  memcpy_fromfs (&tmp_data, &((buf)[c]), 1);
 	  QUEUE_BYTE (midi_out_buf[dev], tmp_data);
 	  c++;
 	}
@@ -482,7 +468,7 @@
       while (c < n)
 	{
 	  REMOVE_BYTE (midi_in_buf[dev], tmp_data);
-	  memcpy_tofs (&((buf)[c]), (&tmp_data), (1));
+	  memcpy_tofs (&((buf)[c]), &tmp_data, 1);
 	  c++;
 	}
     }
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v1.3.35/linux/drivers/sound/mpu401.c	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/mpu401.c	Wed Oct 18 05:33:25 1995
@@ -770,7 +770,7 @@
   switch (cmd)
     {
     case 1:
-      memcpy_fromfs (((char *) init_sequence), &(((char *) arg)[0]), (sizeof (init_sequence)));
+      memcpy_fromfs ((char *) init_sequence, &(((char *) arg)[0]), sizeof (init_sequence));
       return 0;
       break;
 
@@ -789,12 +789,12 @@
 	int             ret;
 	mpu_command_rec rec;
 
-	memcpy_fromfs (((char *) &rec), &(((char *) arg)[0]), (sizeof (rec)));
+	memcpy_fromfs ((char *) &rec, &(((char *) arg)[0]), sizeof (rec));
 
 	if ((ret = mpu401_command (dev, &rec)) < 0)
 	  return ret;
 
-	memcpy_tofs (&(((char *) arg)[0]), ((char *) &rec), (sizeof (rec)));
+	memcpy_tofs ((&((char *) arg)[0]), (char *) &rec, sizeof (rec));
 	return 0;
       }
       break;
@@ -835,7 +835,7 @@
     {
 
     case SNDCTL_SYNTH_INFO:
-      memcpy_tofs (&(((char *) arg)[0]), (&mpu_synth_info[midi_dev]), (sizeof (struct synth_info)));
+      memcpy_tofs ((&((char *) arg)[0]), &mpu_synth_info[midi_dev], sizeof (struct synth_info));
 
       return 0;
       break;
@@ -964,7 +964,7 @@
   midi_synth_send_sysex
 };
 
-static struct synth_operations mpu401_synth_operations[MAX_MIDI_DEV];
+static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV];
 
 static struct midi_operations mpu401_midi_proto =
 {
@@ -1073,15 +1073,27 @@
       if (mpu_cmd (num_midis, 0xE0, 120) >= 0)	/* Set tempo OK */
 	devc->capabilities |= MPU_CAP_INTLG;	/* Supports intelligent mode */
 
+
+  mpu401_synth_operations[num_midis] = (struct synth_operations *) (sound_mem_blocks[sound_num_blocks] = kmalloc (sizeof (struct synth_operations), GFP_KERNEL));
+
+  if (sound_num_blocks < 1024)
+    sound_num_blocks++;;
+
+  if (mpu401_synth_operations[num_midis] == NULL)
+    {
+      printk ("mpu401: Can't allocate memory\n");
+      return mem_start;
+    }
+
   if (!(devc->capabilities & MPU_CAP_INTLG))	/* No intelligent mode */
     {
-      memcpy ((char *) &mpu401_synth_operations[num_midis],
+      memcpy ((char *) mpu401_synth_operations[num_midis],
 	      (char *) &std_midi_synth,
 	      sizeof (struct synth_operations));
     }
   else
     {
-      memcpy ((char *) &mpu401_synth_operations[num_midis],
+      memcpy ((char *) mpu401_synth_operations[num_midis],
 	      (char *) &mpu401_synth_proto,
 	      sizeof (struct synth_operations));
     }
@@ -1091,7 +1103,7 @@
 	  sizeof (struct midi_operations));
 
   mpu401_midi_operations[num_midis].converter =
-    &mpu401_synth_operations[num_midis];
+    mpu401_synth_operations[num_midis];
 
   memcpy ((char *) &mpu_synth_info[num_midis],
 	  (char *) &mpu_synth_info_proto,
@@ -1140,8 +1152,8 @@
   strcpy (mpu401_midi_operations[num_midis].info.name,
 	  mpu_synth_info[num_midis].name);
 
-  mpu401_synth_operations[num_midis].midi_dev = devc->devno = num_midis;
-  mpu401_synth_operations[devc->devno].info =
+  mpu401_synth_operations[num_midis]->midi_dev = devc->devno = num_midis;
+  mpu401_synth_operations[devc->devno]->info =
     &mpu_synth_info[devc->devno];
 
   if (devc->capabilities & MPU_CAP_INTLG)	/* Intelligent mode */
@@ -1262,10 +1274,18 @@
     return 1;
 
   if (inb (hw_config->io_base + 1) == 0xff)
-    return 0;			/* Just bus float? */
+    {
+      DDB (printk ("MPU401: Port %x looks dead.\n", hw_config->io_base));
+      return 0;			/* Just bus float? */
+    }
 
   ok = reset_mpu401 (&tmp_devc);
 
+  if (!ok)
+    {
+      DDB (printk ("MPU401: Reset failed on port %x\n", hw_config->io_base));
+    }
+
   return ok;
 }
 
@@ -1685,7 +1705,7 @@
   if (curr_ticks >= next_event_time)
     {
       next_event_time = 0xffffffff;
-      sequencer_timer ();
+      sequencer_timer (0);
     }
 }
 
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v1.3.35/linux/drivers/sound/opl3.c	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/opl3.c	Wed Oct 18 05:33:25 1995
@@ -130,7 +130,7 @@
       {
 	struct sbi_instrument ins;
 
-	memcpy_fromfs (((char *) &ins), &(((char *) arg)[0]), (sizeof (ins)));
+	memcpy_fromfs ((char *) &ins, &(((char *) arg)[0]), sizeof (ins));
 
 	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
 	  {
@@ -146,7 +146,7 @@
     case SNDCTL_SYNTH_INFO:
       devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
 
-      memcpy_tofs (&(((char *) arg)[0]), (&devc->fm_info), (sizeof (devc->fm_info)));
+      memcpy_tofs ((&((char *) arg)[0]), &devc->fm_info, sizeof (devc->fm_info));
       return 0;
       break;
 
@@ -186,6 +186,17 @@
   if (devc != NULL)
     return 0;
 
+
+  devc = (struct opl_devinfo *) (sound_mem_blocks[sound_num_blocks] = kmalloc (sizeof (*devc), GFP_KERNEL));
+  if (sound_num_blocks < 1024)
+    sound_num_blocks++;;
+
+  if (devc == NULL)
+    {
+      printk ("OPL3: Can't allocate memory for the device control structure\n");
+      return 0;
+    }
+
   devc->osp = osp;
 
   /* Reset timers 1 and 2 */
@@ -876,7 +887,7 @@
       return -EINVAL;
     }
 
-  memcpy_fromfs ((&((char *) &ins)[offs]), &(((char *) addr)[offs]), (sizeof (ins) - offs));
+  memcpy_fromfs (&((char *) &ins)[offs], &(((char *) addr)[offs]), sizeof (ins) - offs);
 
   if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
     {
@@ -1170,19 +1181,9 @@
       return mem_start;
     }
 
-
-  {
-    caddr_t         ptr;
-
-    ptr = sound_mem_blocks[sound_num_blocks] = kmalloc (sizeof (*devc), GFP_KERNEL);
-    if (sound_num_blocks < 1024)
-      sound_num_blocks++;
-    devc = (struct opl_devinfo *) ptr;
-  };
-
   if (devc == NULL)
     {
-      printk ("OPL3: Can't allocate memory for the device control structure\n");
+      printk ("OPL3: Device control structure not initialized.\n");
       return mem_start;
     }
 
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/os.h linux/drivers/sound/os.h
--- v1.3.35/linux/drivers/sound/os.h	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/os.h	Wed Oct 18 05:42:06 1995
@@ -1,6 +1,8 @@
 
 #define ALLOW_SELECT
 #define ALLOW_BUFFER_MAPPING
+#undef NO_INLINE_ASM
+#undef SHORT_BANNERS
 
 #ifdef MODULE
 #include <linux/config.h>
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/patmgr.c linux/drivers/sound/patmgr.c
--- v1.3.35/linux/drivers/sound/patmgr.c	Fri Oct 13 14:44:34 1995
+++ linux/drivers/sound/patmgr.c	Wed Oct 18 05:33:26 1995
@@ -136,7 +136,7 @@
 
       if (mbox[dev] && msg_direction[dev] == A_TO_S)
 	{
-	  memcpy_tofs (&((buf)[0]), ((char *) mbox[dev]), (count));
+	  memcpy_tofs (&((buf)[0]), (char *) mbox[dev], count);
 	  msg_direction[dev] = 0;
 	  ok = 1;
 	}
@@ -161,7 +161,7 @@
       return -EIO;
     }
 
-  memcpy_fromfs ((mbox[dev]), &((buf)[0]), (4));
+  memcpy_fromfs (mbox[dev], &((buf)[0]), 4);
 
   if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
     {
@@ -191,7 +191,7 @@
 
   if (mbox[dev] && !msg_direction[dev])
     {
-      memcpy_fromfs ((&((char *) mbox[dev])[4]), &((buf)[4]), (count - 4));
+      memcpy_fromfs (&((char *) mbox[dev])[4], &((buf)[4]), count - 4);
       msg_direction[dev] = S_TO_A;
 
       if ((appl_wait_flag.mode & WK_SLEEP))
@@ -292,11 +292,7 @@
   else
     {
       if ((mbox[dev] =
-	   (struct patmgr_info *) (
-				    {
-	caddr_t x; x = kmalloc (sizeof (struct patmgr_info), GFP_KERNEL); x;
-				    }
-	   )) == NULL)
+	   (struct patmgr_info *) kmalloc (sizeof (struct patmgr_info), GFP_KERNEL)) == NULL)
 	{
 	  printk ("pmgr: Couldn't allocate memory for a message\n");
 	  return 0;
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v1.3.35/linux/drivers/sound/pss.c	Fri Oct 13 14:44:35 1995
+++ linux/drivers/sound/pss.c	Wed Oct 18 05:33:26 1995
@@ -510,17 +510,11 @@
 	copr_buffer    *buf;
 	int             err;
 
-	buf = (copr_buffer *) (
-				{
-				caddr_t x;
-			     x = kmalloc (sizeof (copr_buffer), GFP_KERNEL);
-				x;
-				}
-	);
+	buf = (copr_buffer *) kmalloc (sizeof (copr_buffer), GFP_KERNEL);
 	if (buf == NULL)
 	  return -ENOSPC;
 
-	memcpy_fromfs (((char *) buf), &(((char *) arg)[0]), (sizeof (*buf)));
+	memcpy_fromfs ((char *) buf, &(((char *) arg)[0]), sizeof (*buf));
 	err = download_boot_block (dev_info, buf);
 	kfree (buf);
 	return err;
@@ -533,7 +527,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	memcpy_fromfs (((char *) &buf), &(((char *) arg)[0]), (sizeof (buf)));
+	memcpy_fromfs ((char *) &buf, &(((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -558,7 +552,7 @@
 	buf.parm1 = tmp;
 	restore_flags (flags);
 
-	memcpy_tofs (&(((char *) arg)[0]), (&buf), (sizeof (buf)));
+	memcpy_tofs ((&((char *) arg)[0]), &buf, sizeof (buf));
 	return 0;
       }
       break;
@@ -569,7 +563,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	memcpy_fromfs (((char *) &buf), &(((char *) arg)[0]), (sizeof (buf)));
+	memcpy_fromfs ((char *) &buf, &(((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -603,7 +597,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	memcpy_fromfs (((char *) &buf), &(((char *) arg)[0]), (sizeof (buf)));
+	memcpy_fromfs ((char *) &buf, &(((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -644,7 +638,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	memcpy_fromfs (((char *) &buf), &(((char *) arg)[0]), (sizeof (buf)));
+	memcpy_fromfs ((char *) &buf, &(((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -678,7 +672,7 @@
 
 	restore_flags (flags);
 
-	memcpy_tofs (&(((char *) arg)[0]), (&buf), (sizeof (buf)));
+	memcpy_tofs ((&((char *) arg)[0]), &buf, sizeof (buf));
 	return 0;
       }
       break;
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sb16_dsp.c linux/drivers/sound/sb16_dsp.c
--- v1.3.35/linux/drivers/sound/sb16_dsp.c	Fri Oct 13 14:44:35 1995
+++ linux/drivers/sound/sb16_dsp.c	Wed Oct 18 05:33:26 1995
@@ -49,6 +49,7 @@
 static int      dsp_current_speed = 8000;
 static int      dsp_busy = 0;
 static int      dma16, dma8;
+static int      trigger_bits = 0x7fffffff;
 static unsigned long dsp_count = 0;
 
 static int      irq_mode = IMODE_NONE;
@@ -232,6 +233,7 @@
 
   irq_mode = IMODE_NONE;
   dsp_busy = 1;
+  trigger_bits = irq_mode;
 
   return 0;
 }
@@ -398,6 +400,11 @@
 static void
 sb16_dsp_trigger (int dev, int bits)
 {
+  if (bits == trigger_bits)	/* No change */
+    return;
+
+  trigger_bits = bits;
+
   if (!bits)
     sb_dsp_command (0xd0);	/* Halt DMA */
   else if (bits & irq_mode)
@@ -572,12 +579,10 @@
     switch (irq_mode)
       {
       case IMODE_OUTPUT:
-	intr_active = 0;
 	DMAbuf_outputintr (my_dev, 1);
 	break;
 
       case IMODE_INPUT:
-	intr_active = 0;
 	DMAbuf_inputintr (my_dev);
 	break;
 
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c
--- v1.3.35/linux/drivers/sound/sb_dsp.c	Mon Oct 16 18:38:24 1995
+++ linux/drivers/sound/sb_dsp.c	Wed Oct 18 05:33:26 1995
@@ -1171,8 +1171,8 @@
 		ess_minor & 0x0f);
     }
 
-   if (snd_set_irq_handler (sbc_irq, sbintr, "SoundBlaster", sb_osp) < 0)
-	  printk ("sb_dsp: Can't allocate IRQ\n");;
+  if (snd_set_irq_handler (sbc_irq, sbintr, "SoundBlaster", sb_osp) < 0)
+    printk ("sb_dsp: Can't allocate IRQ\n");;
 
 #ifndef EXCLUDE_SBPRO
   if (sbc_major >= 3)
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v1.3.35/linux/drivers/sound/sequencer.c	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/sequencer.c	Wed Oct 18 05:33:27 1995
@@ -156,7 +156,7 @@
   while (iqlen && c >= ev_len)
     {
 
-      memcpy_tofs (&((buf)[p]), (&iqueue[iqhead * IEV_SZ]), (ev_len));
+      memcpy_tofs (&((buf)[p]), &iqueue[iqhead * IEV_SZ], ev_len);
       p += ev_len;
       c -= ev_len;
 
@@ -286,7 +286,7 @@
 
   while (c >= 4)
     {
-      memcpy_fromfs ((event), &((buf)[p]), (4));
+      memcpy_fromfs (event, &((buf)[p]), 4);
       ev_code = event[0];
 
       if (ev_code == SEQ_FULLSIZE)
@@ -324,7 +324,7 @@
 	      return count - c;
 	    }
 
-	  memcpy_fromfs ((&event[4]), &((buf)[p + 4]), (4));
+	  memcpy_fromfs (&event[4], &((buf)[p + 4]), 4);
 
 	}
       else
@@ -1763,7 +1763,7 @@
 	struct synth_info inf;
 	int             dev;
 
-	memcpy_fromfs (((char *) &inf), &(((char *) arg)[0]), (sizeof (inf)));
+	memcpy_fromfs ((char *) &inf, &(((char *) arg)[0]), sizeof (inf));
 	dev = inf.device;
 
 	if (dev < 0 || dev >= max_synthdev)
@@ -1781,7 +1781,7 @@
 	struct seq_event_rec event;
 	unsigned long   flags;
 
-	memcpy_fromfs (((char *) &event), &(((char *) arg)[0]), (sizeof (event)));
+	memcpy_fromfs ((char *) &event, &(((char *) arg)[0]), sizeof (event));
 
 	save_flags (flags);
 	cli ();
@@ -1797,13 +1797,13 @@
 	struct midi_info inf;
 	int             dev;
 
-	memcpy_fromfs (((char *) &inf), &(((char *) arg)[0]), (sizeof (inf)));
+	memcpy_fromfs ((char *) &inf, &(((char *) arg)[0]), sizeof (inf));
 	dev = inf.device;
 
 	if (dev < 0 || dev >= max_mididev)
 	  return -ENXIO;
 
-	memcpy_tofs (&(((char *) arg)[0]), ((char *) &(midi_devs[dev]->info)), (sizeof (inf)));
+	memcpy_tofs ((&((char *) arg)[0]), (char *) &(midi_devs[dev]->info), sizeof (inf));
 	return 0;
       }
       break;
@@ -1813,17 +1813,13 @@
 	struct patmgr_info *inf;
 	int             dev, err;
 
-	if ((inf = (struct patmgr_info *) (
-					    {
-		      caddr_t x; x = kmalloc (sizeof (*inf), GFP_KERNEL); x;
-					    }
-	     )) == NULL)
+	if ((inf = (struct patmgr_info *) kmalloc (sizeof (*inf), GFP_KERNEL)) == NULL)
 	  {
 	    printk ("patmgr: Can't allocate memory for a message\n");
 	    return -EIO;
 	  }
 
-	memcpy_fromfs (((char *) inf), &(((char *) arg)[0]), (sizeof (*inf)));
+	memcpy_fromfs ((char *) inf, &(((char *) arg)[0]), sizeof (*inf));
 	dev = inf->device;
 
 	if (dev < 0 || dev >= num_synths)
@@ -1844,7 +1840,7 @@
 	    return err;
 	  }
 
-	memcpy_tofs (&(((char *) arg)[0]), ((char *) inf), (sizeof (*inf)));
+	memcpy_tofs ((&((char *) arg)[0]), (char *) inf, sizeof (*inf));
 	kfree (inf);
 	return 0;
       }
@@ -1855,17 +1851,13 @@
 	struct patmgr_info *inf;
 	int             dev, err;
 
-	if ((inf = (struct patmgr_info *) (
-					    {
-		      caddr_t x; x = kmalloc (sizeof (*inf), GFP_KERNEL); x;
-					    }
-	     )) == NULL)
+	if ((inf = (struct patmgr_info *) kmalloc (sizeof (*inf), GFP_KERNEL)) == NULL)
 	  {
 	    printk ("patmgr: Can't allocate memory for a message\n");
 	    return -EIO;
 	  }
 
-	memcpy_fromfs (((char *) inf), &(((char *) arg)[0]), (sizeof (*inf)));
+	memcpy_fromfs ((char *) inf, &(((char *) arg)[0]), sizeof (*inf));
 	dev = inf->device;
 
 	if (dev < 0 || dev >= num_synths)
@@ -1886,7 +1878,7 @@
 	    return err;
 	  }
 
-	memcpy_tofs (&(((char *) arg)[0]), ((char *) inf), (sizeof (*inf)));
+	memcpy_tofs ((&((char *) arg)[0]), (char *) inf, sizeof (*inf));
 	kfree (inf);
 	return 0;
       }
@@ -1991,7 +1983,7 @@
 
 
 void
-sequencer_timer (void)
+sequencer_timer (unsigned long dummy)
 {
   seq_startplay ();
 }
@@ -2087,23 +2079,13 @@
 
   sequencer_ok = 1;
 
-  {
-    caddr_t         ptr;
-
-    ptr = sound_mem_blocks[sound_num_blocks] = kmalloc (SEQ_MAX_QUEUE * EV_SZ, GFP_KERNEL);
-    if (sound_num_blocks < 1024)
-      sound_num_blocks++;
-    queue = (unsigned char *) ptr;
-  };
-
-  {
-    caddr_t         ptr;
-
-    ptr = sound_mem_blocks[sound_num_blocks] = kmalloc (SEQ_MAX_QUEUE * IEV_SZ, GFP_KERNEL);
-    if (sound_num_blocks < 1024)
-      sound_num_blocks++;
-    iqueue = (unsigned char *) ptr;
-  };
+  queue = (unsigned char *) (sound_mem_blocks[sound_num_blocks] = kmalloc (SEQ_MAX_QUEUE * EV_SZ, GFP_KERNEL));
+  if (sound_num_blocks < 1024)
+    sound_num_blocks++;;
+
+  iqueue = (unsigned char *) (sound_mem_blocks[sound_num_blocks] = kmalloc (SEQ_MAX_QUEUE * IEV_SZ, GFP_KERNEL));
+  if (sound_num_blocks < 1024)
+    sound_num_blocks++;;
 
   return mem_start;
 }
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v1.3.35/linux/drivers/sound/sound_calls.h	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/sound_calls.h	Wed Oct 18 05:33:23 1995
@@ -46,7 +46,7 @@
 	   unsigned int cmd, ioctl_arg arg);
 int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
 long sequencer_init (long mem_start);
-void sequencer_timer(void);
+void sequencer_timer(unsigned long dummy);
 int note_to_freq(int note_num);
 unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
 void seq_input_event(unsigned char *event, int len);
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h
--- v1.3.35/linux/drivers/sound/sound_config.h	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/sound_config.h	Wed Oct 18 05:42:06 1995
@@ -42,36 +42,15 @@
 #include "os.h"
 #include "soundvers.h"
 
-#if !defined(PSS_MPU_BASE) && defined(EXCLUDE_SSCAPE) && \
-      defined(EXCLUDE_TRIX) && !defined(MAD16_MPU_BASE) && \
-      defined(EXCLUDE_CS4232) && defined(EXCLUDE_MAUI)
-#define EXCLUDE_MPU_EMU
-#endif
-
 #if defined(ISC) || defined(SCO) || defined(SVR42)
 #define GENERIC_SYSV
 #endif
 
-/*
- * Disable the AD1848 driver if there are no other drivers requiring it.
- */
-
-#if defined(EXCLUDE_GUS16) && defined(EXCLUDE_MSS) && \
-    defined(EXCLUDE_PSS) && defined(EXCLUDE_GUSMAX) && \
-    defined(EXCLUDE_SSCAPE) && defined(EXCLUDE_TRIX) && defined(EXCLUDE_MAD16) && \
-    defined(EXCLUDE_CS4232) && defined(EXCLUDE_PNP)
-#define EXCLUDE_AD1848
-#endif
 
-#ifdef PSS_MSS_BASE
-#undef EXCLUDE_AD1848
-#endif
 
-#ifdef EXCLUDE_SEQUENCER
-#define EXCLUDE_MIDI
-#define EXCLUDE_YM3812
-#define EXCLUDE_OPL3
-#endif
+
+
+
 
 #ifndef SND_DEFAULT_ENABLE
 #define SND_DEFAULT_ENABLE	1
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c
--- v1.3.35/linux/drivers/sound/sound_switch.c	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/sound_switch.c	Wed Oct 18 05:33:27 1995
@@ -314,7 +314,7 @@
   if (l <= 0)
     return 0;
 
-  memcpy_tofs (&((buf)[0]), (&status_buf[status_ptr]), (l));
+  memcpy_tofs (&((buf)[0]), &status_buf[status_ptr], l);
   status_ptr += l;
 
   return l;
@@ -405,11 +405,7 @@
       if (status_busy)
 	return -EBUSY;
       status_busy = 1;
-      if ((status_buf = (char *) (
-				   {
-			       caddr_t x; x = kmalloc (4000, GFP_KERNEL); x;
-				   }
-	   )) == NULL)
+      if ((status_buf = (char *) kmalloc (4000, GFP_KERNEL)) == NULL)
 	return -EIO;
       status_len = status_ptr = 0;
       init_status ();
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sound_timer.c linux/drivers/sound/sound_timer.c
--- v1.3.35/linux/drivers/sound/sound_timer.c	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/sound_timer.c	Wed Oct 18 05:33:27 1995
@@ -326,7 +326,7 @@
   if (curr_ticks >= next_event_time)
     {
       next_event_time = 0xffffffff;
-      sequencer_timer ();
+      sequencer_timer (0);
     }
 }
 
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v1.3.35/linux/drivers/sound/soundcard.c	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/soundcard.c	Wed Oct 18 05:36:05 1995
@@ -36,11 +36,8 @@
 
 #include <linux/major.h>
 
-#ifndef EXCLUDE_PNP
-#include <linux/pnp.h>
-#endif
-
 static int      soundcards_installed = 0;	/* Number of installed cards */
+static int      chrdev_registered = 0;
 
 /*
  * Table for permanently allocated memory (used when unloading the module)
@@ -348,16 +345,13 @@
 {
 #ifndef MODULE
   register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+  chrdev_registered = 1;
 #endif
 
   soundcard_configured = 1;
 
   sndtable_init (0);		/* Initialize call tables and
 				   * detect cards */
-#ifndef EXCLUDE_PNP
-  sound_pnp_init ();
-#endif
-
   if (!(soundcards_installed = sndtable_get_cardcount ()))
     return;			/* No cards detected */
 
@@ -432,6 +426,7 @@
       return err;
     }
 
+  chrdev_registered = 1;
   soundcard_init ();
 
   if (sound_num_blocks >= 1024)
@@ -450,7 +445,8 @@
     {
       int             i;
 
-      unregister_chrdev (SOUND_MAJOR, "sound");
+      if (chrdev_registered)
+	unregister_chrdev (SOUND_MAJOR, "sound");
 
       sound_stop_timer ();
       sound_unload_drivers ();
@@ -467,10 +463,6 @@
 	    sound_free_dma (i);
 	  }
 
-#ifndef EXCLUDE_PNP
-      sound_pnp_disconnect ();
-#endif
-
     }
 }
 #endif
@@ -571,6 +563,11 @@
 }
 
 #ifndef EXCLUDE_SEQUENCER
+
+
+static struct timer_list seq_timer =
+{NULL, NULL, 0, 0, sequencer_timer};
+
 void
 request_sound_timer (int count)
 {
@@ -580,9 +577,12 @@
     count = jiffies + (-count);
   else
     count += seq_time;
-  timer_table[SOUND_TIMER].fn = sequencer_timer;
-  timer_table[SOUND_TIMER].expires = count;
-  timer_active |= 1 << SOUND_TIMER;
+
+
+  {
+    seq_timer.expires = ((count - jiffies)) + jiffies;
+    add_timer (&seq_timer);
+  };
 }
 
 #endif
@@ -590,8 +590,7 @@
 void
 sound_stop_timer (void)
 {
-  timer_table[SOUND_TIMER].expires = 0;
-  timer_active &= ~(1 << SOUND_TIMER);
+  del_timer (&seq_timer);;
 }
 
 #ifndef EXCLUDE_AUDIO
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c
--- v1.3.35/linux/drivers/sound/sscape.c	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/sscape.c	Wed Oct 18 05:33:27 1995
@@ -693,16 +693,10 @@
 	copr_buffer    *buf;
 	int             err;
 
-	buf = (copr_buffer *) (
-				{
-				caddr_t x;
-			     x = kmalloc (sizeof (copr_buffer), GFP_KERNEL);
-				x;
-				}
-	);
+	buf = (copr_buffer *) kmalloc (sizeof (copr_buffer), GFP_KERNEL);
 	if (buf == NULL)
 	  return -ENOSPC;
-	memcpy_fromfs (((char *) buf), &(((char *) arg)[0]), (sizeof (*buf)));
+	memcpy_fromfs ((char *) buf, &(((char *) arg)[0]), sizeof (*buf));
 	err = download_boot_block (dev_info, buf);
 	kfree (buf);
 	return err;
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/sys_timer.c linux/drivers/sound/sys_timer.c
--- v1.3.35/linux/drivers/sound/sys_timer.c	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/sys_timer.c	Wed Oct 18 05:33:27 1995
@@ -80,7 +80,7 @@
 	  if (curr_ticks >= next_event_time)
 	    {
 	      next_event_time = 0xffffffff;
-	      sequencer_timer ();
+	      sequencer_timer (0);
 	    }
 	}
     }
diff -u --recursive --new-file v1.3.35/linux/drivers/sound/trix.c linux/drivers/sound/trix.c
--- v1.3.35/linux/drivers/sound/trix.c	Fri Oct 13 14:44:44 1995
+++ linux/drivers/sound/trix.c	Wed Oct 18 05:33:27 1995
@@ -353,13 +353,22 @@
   {-1, -1, -1, 1, 2, 3, -1, 4, -1, 5};
 
   if (!kilroy_was_here)
-    return 0;			/* AudioTriX Pro has not been detected earlier */
+    {
+      DDB (printk ("Trix: WSS and SB modes must be initialized before MPU\n"));
+      return 0;			/* AudioTriX Pro has not been detected earlier */
+    }
 
   if (!sb_initialized)
-    return 0;
+    {
+      DDB (printk ("Trix: SB mode must be initialized before MPU\n"));
+      return 0;
+    }
 
   if (mpu_initialized)
-    return 0;
+    {
+      DDB (printk ("Trix: MPU mode already initialized\n"));
+      return 0;
+    }
 
   if (check_region (hw_config->io_base, 4))
     {
@@ -368,10 +377,16 @@
     }
 
   if (hw_config->irq > 9)
-    return 0;
+    {
+      printk ("AudioTriX: Bad MPU IRQ %d\n", hw_config->irq);
+      return 0;
+    }
 
   if (irq_bits[hw_config->irq] == -1)
-    return 0;
+    {
+      printk ("AudioTriX: Bad MPU IRQ %d\n", hw_config->irq);
+      return 0;
+    }
 
   switch (hw_config->io_base)
     {
diff -u --recursive --new-file v1.3.35/linux/fs/Config.in linux/fs/Config.in
--- v1.3.35/linux/fs/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/fs/Config.in	Sun Oct 22 16:02:13 1995
@@ -0,0 +1,22 @@
+#
+# Filesystem configuration
+#
+mainmenu_option next_comment
+comment 'Filesystems'
+
+tristate 'Standard (minix) fs support' CONFIG_MINIX_FS
+tristate 'Extended fs support' CONFIG_EXT_FS
+tristate 'Second extended fs support' CONFIG_EXT2_FS
+tristate 'xiafs filesystem support' CONFIG_XIA_FS
+tristate 'msdos fs support' CONFIG_MSDOS_FS
+if [ "$CONFIG_MSDOS_FS" != "n" ]; then
+  tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS
+fi
+bool '/proc filesystem support' CONFIG_PROC_FS
+if [ "$CONFIG_INET" = "y" ]; then
+  tristate 'NFS filesystem support' CONFIG_NFS_FS
+fi
+tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
+tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
+tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
+tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS
diff -u --recursive --new-file v1.3.35/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v1.3.35/linux/fs/binfmt_elf.c	Mon Oct 16 18:38:24 1995
+++ linux/fs/binfmt_elf.c	Sun Oct 22 14:49:40 1995
@@ -621,7 +621,11 @@
 			if(k > start_code) start_code = k;
 			k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
 			if(k > elf_bss) elf_bss = k;
+#if 1
 			if((elf_ppnt->p_flags | PF_W) && end_code <  k)
+#else
+			if( !(elf_ppnt->p_flags & PF_W) && end_code <  k)
+#endif
 				end_code = k; 
 			if(end_data < k) end_data = k; 
 			k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
diff -u --recursive --new-file v1.3.35/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.35/linux/fs/buffer.c	Fri Oct 13 14:44:44 1995
+++ linux/fs/buffer.c	Mon Oct 23 10:00:31 1995
@@ -769,10 +769,14 @@
 }
 
 
-void refile_buffer(struct buffer_head * buf){
+void refile_buffer(struct buffer_head * buf)
+{
 	int dispose;
-	if(buf->b_dev == B_FREE)
-		panic("Attempt to refile free buffer\n");
+
+	if(buf->b_dev == B_FREE) {
+		printk("Attempt to refile free buffer\n");
+		return;
+	}
 	if (buf->b_dirt)
 		dispose = BUF_DIRTY;
 	else if (mem_map[MAP_NR((unsigned long) buf->b_data)].count > 1)
@@ -800,6 +804,9 @@
 	}
 }
 
+/*
+ * Release a buffer head
+ */
 void brelse(struct buffer_head * buf)
 {
 	if (!buf)
@@ -811,23 +818,35 @@
 	refile_buffer(buf);
 
 	if (buf->b_count) {
-		if (--buf->b_count)
-			return;
-		wake_up(&buffer_wait);
-		if (buf->b_reuse) {
-			buf->b_reuse = 0;
-			if (!buf->b_lock && !buf->b_dirt && 
-			    !buf->b_wait && buf->b_uptodate) {
-				if(buf->b_dev == B_FREE)
-					panic("brelse: Wrong list");
-				remove_from_queues(buf);
-				buf->b_dev = B_FREE;
-				put_last_free(buf);
-			}
-		}
+		if (!--buf->b_count)
+			wake_up(&buffer_wait);
 		return;
 	}
 	printk("VFS: brelse: Trying to free free buffer\n");
+}
+
+/*
+ * bforget() is like brelse(), except is throws the buffer away
+ */
+void bforget(struct buffer_head * buf)
+{
+	if (!buf)
+		return;
+	wait_on_buffer(buf);
+	if (buf->b_count != 1) {
+		printk("Aieee... bforget(): count = %d\n", buf->b_count);
+		return;
+	}
+	if (mem_map[MAP_NR(buf->b_data)].count != 1) {
+		printk("Aieee... bforget(): shared buffer\n");
+		return;
+	}
+	mark_buffer_clean(buf);
+	buf->b_count = 0;
+	remove_from_queues(buf);
+	buf->b_dev = B_FREE;
+	put_last_free(buf);
+	wake_up(&buffer_wait);
 }
 
 /*
diff -u --recursive --new-file v1.3.35/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c
--- v1.3.35/linux/fs/ext2/truncate.c	Tue Aug  8 12:31:39 1995
+++ linux/fs/ext2/truncate.c	Sun Oct 22 21:38:58 1995
@@ -27,9 +27,21 @@
 #include <linux/locks.h>
 #include <linux/string.h>
 
+#if 0
+
+/*
+ * Secure deletion currently doesn't work. It interacts very badly
+ * with buffers shared with memory mappings, and for that reason
+ * can't be done in the truncate() routines. It should instead be
+ * done separately in "release()" before calling the truncate routines
+ * that will release the actual file blocks.
+ *
+ *		Linus
+ */
 static int ext2_secrm_seed = 152;	/* Random generator base */
 
 #define RANDOM_INT (ext2_secrm_seed = ext2_secrm_seed * 69069l +1)
+#endif
 
 /*
  * Truncate has the most races in the whole filesystem: coding it is
@@ -63,12 +75,8 @@
 		tmp = *p;
 		if (!tmp)
 			continue;
-		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
-			bh = getblk (inode->i_dev, tmp,
+		bh = get_hash_table (inode->i_dev, tmp,
 				     inode->i_sb->s_blocksize);
-		else
-			bh = get_hash_table (inode->i_dev, tmp,
-					     inode->i_sb->s_blocksize);
 		if (i < direct_block) {
 			brelse (bh);
 			goto repeat;
@@ -81,15 +89,7 @@
 		*p = 0;
 		inode->i_blocks -= blocks;
 		inode->i_dirt = 1;
-		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
-			memset(bh->b_data, RANDOM_INT, inode->i_sb->s_blocksize);
-			mark_buffer_dirty(bh, 1);
-		}
-		else if (bh) {
-			mark_buffer_clean(bh);
-			bh->b_reuse = 1;
-		}
-		brelse (bh);
+		bforget(bh);
 		if (free_count == 0) {
 			block_to_free = tmp;
 			free_count++;
@@ -143,12 +143,8 @@
 		tmp = *ind;
 		if (!tmp)
 			continue;
-		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
-			bh = getblk (inode->i_dev, tmp,
+		bh = get_hash_table (inode->i_dev, tmp,
 				     inode->i_sb->s_blocksize);
-		else
-			bh = get_hash_table (inode->i_dev, tmp,
-					     inode->i_sb->s_blocksize);
 		if (i < indirect_block) {
 			brelse (bh);
 			goto repeat;
@@ -160,15 +156,7 @@
 		}
 		*ind = 0;
 		mark_buffer_dirty(ind_bh, 1);
-		if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
-			memset(bh->b_data, RANDOM_INT, inode->i_sb->s_blocksize);
-			mark_buffer_dirty(bh, 1);
-		}
-		else if (bh) {
-			mark_buffer_clean(bh);
-			bh->b_reuse = 1;
-		}
-		brelse (bh);
+		bforget(bh);
 		if (free_count == 0) {
 			block_to_free = tmp;
 			free_count++;
diff -u --recursive --new-file v1.3.35/linux/fs/msdos/misc.c linux/fs/msdos/misc.c
--- v1.3.35/linux/fs/msdos/misc.c	Mon Sep 18 14:54:07 1995
+++ linux/fs/msdos/misc.c	Mon Oct 23 08:51:47 1995
@@ -120,7 +120,7 @@
 int msdos_add_cluster(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
-	int count,nr,limit,last,current,sector,last_sector,file_cluster;
+	int count,nr,limit,last,curr,sector,last_sector,file_cluster;
 	struct buffer_head *bh;
 	int cluster_size = MSDOS_SB(inode->i_sb)->cluster_size;
 
@@ -164,14 +164,14 @@
 	   update the cache.
 	*/
 	file_cluster = 0;
-	if ((current = MSDOS_I(inode)->i_start) != 0) {
-		cache_lookup(inode,INT_MAX,&last,&current);
+	if ((curr = MSDOS_I(inode)->i_start) != 0) {
+		cache_lookup(inode,INT_MAX,&last,&curr);
 		file_cluster = last;
-		while (current && current != -1){
+		while (curr && curr != -1){
 			PRINTK (("."));
 			file_cluster++;
-			if (!(current = fat_access(inode->i_sb,
-			    last = current,-1))) {
+			if (!(curr = fat_access(inode->i_sb,
+			    last = curr,-1))) {
 				fs_panic(inode->i_sb,"File without EOF");
 				return -ENOSPC;
 			}
@@ -480,24 +480,24 @@
 int msdos_parent_ino(struct inode *dir,int locked)
 {
 	static int zero = 0;
-	int error,current,prev,nr;
+	int error,curr,prev,nr;
 
 	if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
 	if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
 	if (!locked) lock_creation(); /* prevent renames */
-	if ((current = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
+	if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
 	    &zero,NULL,NULL,NULL)) < 0) {
 		if (!locked) unlock_creation();
-		return current;
+		return curr;
 	}
-	if (!current) nr = MSDOS_ROOT_INO;
+	if (!curr) nr = MSDOS_ROOT_INO;
 	else {
-		if ((prev = raw_scan(dir->i_sb,current,MSDOS_DOTDOT,&zero,NULL,
+		if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
 		    NULL,NULL)) < 0) {
 			if (!locked) unlock_creation();
 			return prev;
 		}
-		if ((error = raw_scan(dir->i_sb,prev,NULL,&current,&nr,NULL,
+		if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
 		    NULL)) < 0) {
 			if (!locked) unlock_creation();
 			return error;
diff -u --recursive --new-file v1.3.35/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c
--- v1.3.35/linux/fs/nfs/rpcsock.c	Fri Oct  6 14:35:16 1995
+++ linux/fs/nfs/rpcsock.c	Mon Oct 23 10:55:57 1995
@@ -37,7 +37,7 @@
 #include <linux/rpcsock.h>
 
 #define msleep(sec)	{ current->timeout = sec * HZ / 1000; \
-			  current->state = TAKS_INTERRUPTIBLE; \
+			  current->state = TASK_INTERRUPTIBLE; \
 			  schedule(); \
 			}
 #define dprintk		if (0) printk
@@ -181,7 +181,6 @@
 	do {
 		/* We are not the receiver. Wait on the side lines. */
 		if (rsock->head != slot) {
-			slot->wait = NULL;
 			interruptible_sleep_on(&slot->wait);
 			if (slot->gotit)
 				break;
@@ -328,7 +327,7 @@
 		rsock->free = slot;
 
 		/* wake up tasks that haven't sent anything yet. (Waking
-		 * up the first one the wait queue would be enough) */
+		 * up the first one on the wait queue would be enough) */
 		if (rsock->backlog)
 			wake_up(&rsock->backlog);
 	}
@@ -349,6 +348,7 @@
 	dprintk("RPC: make RPC socket...\n");
 	if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL)
 		return NULL;
+	memset(rsock, 0, sizeof(*rsock)); /* Nnnngh! */
 
 	rsock->sock = &file->f_inode->u.socket_i;
 	rsock->file = file;
@@ -358,11 +358,13 @@
 		slot->next = slot + 1;
 	slot->next = NULL;
 
+	/* --- taken care of by memset above ---
 	rsock->backlog = NULL;
 	rsock->head = rsock->tail = NULL;
 
 	rsock->shutwait = NULL;
 	rsock->shutdown = 0;
+	 */
 
 	dprintk("RPC: made socket %08lx", (long) rsock);
 	return rsock;
@@ -374,7 +376,7 @@
 	unsigned long	t0 = jiffies;
 
 	rsock->shutdown = 1;
-	while (rsock->head) {
+	while (rsock->head || rsock->backlog) {
 		interruptible_sleep_on(&rsock->shutwait);
 		if (current->signal & ~current->blocked)
 			return -EINTR;
diff -u --recursive --new-file v1.3.35/linux/fs/open.c linux/fs/open.c
--- v1.3.35/linux/fs/open.c	Sun Sep  3 12:27:01 1995
+++ linux/fs/open.c	Sun Oct 22 21:10:12 1995
@@ -61,11 +61,25 @@
 	return 0;
 }
 
-asmlinkage int sys_truncate(const char * path, unsigned int length)
+static int do_truncate(struct inode *inode, unsigned long length)
+{
+	struct iattr newattrs;
+
+	/* truncate virtual mappings of this file */
+	vmtruncate(inode, length);
+	inode->i_size = newattrs.ia_size = length;
+	if (inode->i_op && inode->i_op->truncate)
+		inode->i_op->truncate(inode);
+	newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME;
+	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME;
+	inode->i_dirt = 1;
+	return notify_change(inode, &newattrs);
+}
+
+asmlinkage int sys_truncate(const char * path, unsigned long length)
 {
 	struct inode * inode;
 	int error;
-	struct iattr newattrs;
 
 	error = namei(path,&inode);
 	if (error)
@@ -91,23 +105,16 @@
 		iput(inode);
 		return error;
 	}
-	inode->i_size = newattrs.ia_size = length;
-	if (inode->i_op && inode->i_op->truncate)
-		inode->i_op->truncate(inode);
-	newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME;
-	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME;
-	inode->i_dirt = 1;
-	error = notify_change(inode, &newattrs);
+	error = do_truncate(inode, length);
 	put_write_access(inode);
 	iput(inode);
 	return error;
 }
 
-asmlinkage int sys_ftruncate(unsigned int fd, unsigned int length)
+asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length)
 {
 	struct inode * inode;
 	struct file * file;
-	struct iattr newattrs;
 
 	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 		return -EBADF;
@@ -117,13 +124,7 @@
 		return -EACCES;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		return -EPERM;
-	inode->i_size = newattrs.ia_size = length;
-	if (inode->i_op && inode->i_op->truncate)
-		inode->i_op->truncate(inode);
-	newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME;
-	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME;
-	inode->i_dirt = 1;
-	return notify_change(inode, &newattrs);
+	return do_truncate(inode, length);
 }
 
 /* If times==NULL, set access and modification to current time,
diff -u --recursive --new-file v1.3.35/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.35/linux/fs/proc/array.c	Tue Oct 10 18:46:38 1995
+++ linux/fs/proc/array.c	Sat Oct 21 13:33:56 1995
@@ -405,6 +405,7 @@
 	struct task_struct ** p = get_task(pid), *tsk;
 	unsigned long sigignore=0, sigcatch=0, wchan;
 	unsigned long vsize, eip, esp;
+	long priority, nice;
 	int i,tty_pgrp;
 	char state;
 
@@ -445,6 +446,12 @@
 		tty_pgrp = tsk->tty->pgrp;
 	else
 		tty_pgrp = -1;
+
+	/* scale priority and nice values from timeslices to 0..40 */
+	priority = tsk->counter;
+	priority = (priority * 10 + 5) / DEF_PRIORITY;
+	nice = tsk->priority;
+	nice = (nice * 20 + 10) / DEF_PRIORITY;
 	return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
 %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu \
 %lu %lu %lu %lu\n",
@@ -465,10 +472,10 @@
 		tsk->stime,
 		tsk->cutime,
 		tsk->cstime,
-		tsk->counter,  /* this is the kernel priority ---
-				   subtract 30 in your user-level program. */
-		tsk->priority, /* this is the nice value ---
-				   subtract 15 in your user-level program. */
+		priority,  /* this is the kernel priority ---
+				   subtract 20 in your user-level program. */
+		nice,	   /* this is the nice value ---
+				   subtract 20 in your user-level program. */
 		tsk->timeout,
 		tsk->it_real_value,
 		tsk->start_time,
diff -u --recursive --new-file v1.3.35/linux/fs/read_write.c linux/fs/read_write.c
--- v1.3.35/linux/fs/read_write.c	Wed Oct  4 14:14:33 1995
+++ linux/fs/read_write.c	Fri Oct 20 11:33:02 1995
@@ -9,6 +9,7 @@
 #include <linux/stat.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/uio.h>
 
@@ -151,46 +152,93 @@
 	return written;
 }
 
-/*
- * OSF/1 (and SunOS) readv/writev emulation.
- *
- * NOTE! This is not really the way it should be done,
- * but it should be good enough for TCP connections,
- * notably X11 ;-)
- */
-asmlinkage int sys_readv(unsigned long fd, const struct iovec * vector, long count)
+static int sock_readv_writev(int type, struct inode * inode, struct file * file,
+	const struct iovec * iov, long count, long size)
 {
-	int retval;
-	struct file * file;
-	struct inode * inode;
+	struct msghdr msg;
+	struct socket *sock;
 
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode))
-		return -EBADF;
-	if (!(file->f_mode & 1))
-		return -EBADF;
-	if (!file->f_op || !file->f_op->read)
-		return -EINVAL;
+	sock = &inode->u.socket_i;
+	if (!sock->ops)
+		return -EOPNOTSUPP;
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_accrights = NULL;
+	msg.msg_iov = (struct iovec *) iov;
+	msg.msg_iovlen = count;
+
+	/* read() does a VERIFY_WRITE */
+	if (type == VERIFY_WRITE) {
+		if (!sock->ops->recvmsg)
+			return -EOPNOTSUPP;
+		return sock->ops->recvmsg(sock, &msg, size,
+			(file->f_flags & O_NONBLOCK), 0, NULL);
+	}
+	if (!sock->ops->sendmsg)
+		return -EOPNOTSUPP;
+	return sock->ops->sendmsg(sock, &msg, size,
+		(file->f_flags & O_NONBLOCK), 0);
+}
+
+typedef int (*IO_fn_t)(struct inode *, struct file *, char *, int);
+
+static int do_readv_writev(int type, struct inode * inode, struct file * file,
+	const struct iovec * vector, unsigned long count)
+{
+	size_t tot_len;
+	struct iovec iov[MAX_IOVEC];
+	int retval, i;
+	IO_fn_t fn;
+
+	/*
+	 * First get the "struct iovec" from user memory and
+	 * verify all the pointers
+	 */
 	if (!count)
 		return 0;
+	if (count > MAX_IOVEC)
+		return -EINVAL;
 	retval = verify_area(VERIFY_READ, vector, count*sizeof(*vector));
 	if (retval)
 		return retval;
+	memcpy_fromfs(iov, vector, count*sizeof(*vector));
+	tot_len = 0;
+	for (i = 0 ; i < count ; i++) {
+		tot_len += iov[i].iov_len;
+		retval = verify_area(type, iov[i].iov_base, iov[i].iov_len);
+		if (retval)
+			return retval;
+	}
+
+	/*
+	 * Then do the actual IO.  Note that sockets need to be handled
+	 * specially as they have atomicity guarantees and can handle
+	 * iovec's natively
+	 */
+	if (inode->i_sock)
+		return sock_readv_writev(type, inode, file, iov, count, tot_len);
 
+	if (!file->f_op)
+		return -EINVAL;
+	/* VERIFY_WRITE actually means a read, as we write to user space */
+	fn = file->f_op->read;
+	if (type == VERIFY_READ)
+		fn = (IO_fn_t) file->f_op->write;		
+	vector = iov;
 	while (count > 0) {
 		void * base;
 		int len, nr;
 
-		base = get_user(&vector->iov_base);
-		len = get_user(&vector->iov_len);
+		base = vector->iov_base;
+		len = vector->iov_len;
 		vector++;
 		count--;
-		nr = verify_area(VERIFY_WRITE, base, len);
-		if (!nr)
-			nr = file->f_op->read(inode, file, base, len);
+		nr = fn(inode, file, base, len);
 		if (nr < 0) {
 			if (retval)
-				return retval;
-			return nr;
+				break;
+			retval = nr;
+			break;
 		}
 		retval += nr;
 		if (nr != len)
@@ -199,43 +247,26 @@
 	return retval;
 }
 
-asmlinkage int sys_writev(unsigned long fd, const struct iovec * vector, long count)
+asmlinkage int sys_readv(unsigned long fd, const struct iovec * vector, long count)
 {
-	int retval;
 	struct file * file;
 	struct inode * inode;
 
 	if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode))
 		return -EBADF;
-	if (!(file->f_mode & 2))
+	if (!(file->f_mode & 1))
 		return -EBADF;
-	if (!file->f_op || !file->f_op->write)
-		return -EINVAL;
-	if (!count)
-		return 0;
-	retval = verify_area(VERIFY_READ, vector, count*sizeof(*vector));
-	if (retval)
-		return retval;
+	return do_readv_writev(VERIFY_WRITE, inode, file, vector, count);
+}
 
-	while (count > 0) {
-		void * base;
-		int len, nr;
+asmlinkage int sys_writev(unsigned long fd, const struct iovec * vector, long count)
+{
+	struct file * file;
+	struct inode * inode;
 
-		base = get_user(&vector->iov_base);
-		len = get_user(&vector->iov_len);
-		vector++;
-		count--;
-		nr = verify_area(VERIFY_READ, base, len);
-		if (!nr)
-			nr = file->f_op->write(inode, file, base, len);
-		if (nr < 0) {
-			if (retval)
-				return retval;
-			return nr;
-		}
-		retval += nr;
-		if (nr != len)
-			break;
-	}
-	return retval;
+	if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode))
+		return -EBADF;
+	if (!(file->f_mode & 2))
+		return -EBADF;
+	return do_readv_writev(VERIFY_READ, inode, file, vector, count);
 }
diff -u --recursive --new-file v1.3.35/linux/include/asm-alpha/floppy.h linux/include/asm-alpha/floppy.h
--- v1.3.35/linux/include/asm-alpha/floppy.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-alpha/floppy.h	Mon Oct 23 13:12:55 1995
@@ -0,0 +1,59 @@
+/*
+ * Architecture specific parts of the Floppy driver
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995
+ */
+#ifndef __ASM_ALPHA_FLOPPY_H
+#define __ASM_ALPHA_FLOPPY_H
+
+#define fd_inb(port)			inb_p(port)
+#define fd_outb(port,value)		outb_p(port,value)
+
+#define fd_enable_dma()         enable_dma(FLOPPY_DMA)
+#define fd_disable_dma()        disable_dma(FLOPPY_DMA)
+#define fd_request_dma()        request_dma(FLOPPY_DMA,"floppy")
+#define fd_free_dma()           free_dma(FLOPPY_DMA)
+#define fd_clear_dma_ff()       clear_dma_ff(FLOPPY_DMA)
+#define fd_set_dma_mode(mode)   set_dma_mode(FLOPPY_DMA,mode)
+#define fd_set_dma_addr(addr)   set_dma_addr(FLOPPY_DMA,addr)
+#define fd_set_dma_count(count) set_dma_count(FLOPPY_DMA,count)
+#define fd_enable_irq()         enable_irq(FLOPPY_IRQ)
+#define fd_disable_irq()        disable_irq(FLOPPY_IRQ)
+#define fd_cacheflush(addr,size) /* nothing */
+#define fd_request_irq()        request_irq(FLOPPY_IRQ, floppy_interrupt, \
+					    SA_INTERRUPT|SA_SAMPLE_RANDOM, \
+				            "floppy")
+#define fd_free_irq()           free_irq(FLOPPY_IRQ);
+
+__inline__ void virtual_dma_init(void)
+{
+	/* Nothing to do on an Alpha */
+}
+
+#ifdef MODULE
+int FDC1 = 0x3f0;
+int FDC2 = -1;
+#else
+#define FDC1 0x3f0
+static int FDC2 = -1;
+#endif
+
+/*
+ * Again, the CMOS information doesn't work on the alpha..
+ */
+#define FLOPPY0_TYPE 6
+#define FLOPPY1_TYPE 0
+
+#define N_FDC 2
+#define N_DRIVE 8
+
+/*
+ * The Alpha has no problems with floppy DMA crossing 64k borders.
+ */
+#define CROSS_64KB(a,s)	(0)
+
+#endif /* __ASM_ALPHA_FLOPPY_H */
diff -u --recursive --new-file v1.3.35/linux/include/asm-alpha/system.h linux/include/asm-alpha/system.h
--- v1.3.35/linux/include/asm-alpha/system.h	Fri Oct  6 14:35:16 1995
+++ linux/include/asm-alpha/system.h	Mon Oct 23 09:13:44 1995
@@ -55,7 +55,7 @@
 extern void alpha_switch_to(unsigned long pctxp);
 
 #define switch_to(p) do { \
-	current = p; \
+	current_set[0] = p; \
 	alpha_switch_to((unsigned long) &(p)->tss - 0xfffffc0000000000); \
 } while (0)
 
diff -u --recursive --new-file v1.3.35/linux/include/asm-i386/floppy.h linux/include/asm-i386/floppy.h
--- v1.3.35/linux/include/asm-i386/floppy.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-i386/floppy.h	Mon Oct 23 13:12:41 1995
@@ -0,0 +1,61 @@
+/*
+ * Architecture specific parts of the Floppy driver
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995
+ */
+#ifndef __ASM_I386_FLOPPY_H
+#define __ASM_I386_FLOPPY_H
+
+#define fd_inb(port)			inb_p(port)
+#define fd_outb(port,value)		outb_p(port,value)
+
+#define fd_enable_dma()         enable_dma(FLOPPY_DMA)
+#define fd_disable_dma()        disable_dma(FLOPPY_DMA)
+#define fd_request_dma()        request_dma(FLOPPY_DMA,"floppy")
+#define fd_free_dma()           free_dma(FLOPPY_DMA)
+#define fd_clear_dma_ff()       clear_dma_ff(FLOPPY_DMA)
+#define fd_set_dma_mode(mode)   set_dma_mode(FLOPPY_DMA,mode)
+#define fd_set_dma_addr(addr)   set_dma_addr(FLOPPY_DMA,addr)
+#define fd_set_dma_count(count) set_dma_count(FLOPPY_DMA,count)
+#define fd_enable_irq()         enable_irq(FLOPPY_IRQ)
+#define fd_disable_irq()        disable_irq(FLOPPY_IRQ)
+#define fd_cacheflush(addr,size) /* nothing */
+#define fd_request_irq()        request_irq(FLOPPY_IRQ, floppy_interrupt, \
+					    SA_INTERRUPT|SA_SAMPLE_RANDOM, \
+				            "floppy")
+#define fd_free_irq()           free_irq(FLOPPY_IRQ);
+
+__inline__ void virtual_dma_init(void)
+{
+	/* Nothing to do on an i386 */
+}
+
+#ifdef MODULE
+int FDC1 = 0x3f0;
+int FDC2 = -1;
+#else
+#define FDC1 0x3f0
+static int FDC2 = -1;
+#endif
+
+#define FLOPPY0_TYPE	((CMOS_READ(0x10) >> 4) & 15)
+#define FLOPPY1_TYPE	(CMOS_READ(0x10) & 15)
+
+#define N_FDC 2
+#define N_DRIVE 8
+
+/*
+ * The DMA channel used by the floppy controller cannot access data at
+ * addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
+ */
+#define CROSS_64KB(a,s) ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64)
+
+#endif /* __ASM_I386_FLOPPY_H */
diff -u --recursive --new-file v1.3.35/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v1.3.35/linux/include/asm-i386/smp.h	Wed Oct  4 14:14:33 1995
+++ linux/include/asm-i386/smp.h	Tue Oct 17 13:42:35 1995
@@ -228,5 +228,18 @@
 #endif /* !ASSEMBLY */
 
 #define NO_PROC_ID		0xFF		/* No processor magic marker */
+
+/*
+ *	This magic constant controls our willingness to transfer
+ *	a process across CPUs. Such a transfer incurs misses on the L1
+ *	cache, and on a P6 or P5 with multiple L2 caches L2 hits. My
+ *	gut feeling is this will vary by board in value. For a board
+ *	with seperate L2 cache it probably depends also on the RSS, and
+ *	for a board with shared L2 cache it ought to decay fast as other
+ *	processes are run.
+ */
+ 
+#define PROC_CHANGE_PENALTY	5		/* Schedule penalty */
+
 #endif
 #endif
diff -u --recursive --new-file v1.3.35/linux/include/asm-mips/floppy.h linux/include/asm-mips/floppy.h
--- v1.3.35/linux/include/asm-mips/floppy.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-mips/floppy.h	Fri Oct 20 14:36:33 1995
@@ -0,0 +1,89 @@
+/*
+ * Architecture specific parts of the Floppy driver
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995
+ */
+#ifndef __ASM_MIPS_FLOPPY_H
+#define __ASM_MIPS_FLOPPY_H
+
+#include <linux/config.h>
+
+#include <asm/bootinfo.h>
+#include <asm/jazz.h>
+#include <asm/jazzdma.h>
+#include <asm/mipsconfig.h>
+#include <asm/vector.h>
+
+#define fd_inb(port)			feature->fd_inb(port)
+#define fd_outb(port,value)		feature->fd_outb(port,value)
+
+#define fd_enable_dma()			feature->fd_enable_dma()
+#define fd_disable_dma()		feature->fd_disable_dma()
+#define fd_request_dma()		feature->fd_request_dma()
+#define fd_free_dma()			feature->fd_free_dma()
+#define fd_clear_dma_ff()		feature->fd_clear_dma_ff()
+#define fd_set_dma_mode(mode)		feature->fd_set_dma_mode(mode)
+#define fd_set_dma_addr(addr)		feature->fd_set_dma_addr(addr)
+#define fd_set_dma_count(count)		feature->fd_set_dma_count(count)
+#define fd_get_dma_residue()		feature->fd_get_dma_residue()
+#define fd_enable_irq()			feature->fd_enable_irq()
+#define fd_disable_irq()		feature->fd_disable_irq()
+#define fd_cacheflush(addr, size)	feature->fd_cacheflush((void *)addr, size)
+#define fd_request_irq()        request_irq(FLOPPY_IRQ, floppy_interrupt, \
+					    SA_INTERRUPT|SA_SAMPLE_RANDOM, \
+				            "floppy")
+#define fd_free_irq()           free_irq(FLOPPY_IRQ);
+
+#define virtual_dma_init()                                              \
+        if (boot_info.machtype == MACH_ACER_PICA_61 ||                  \
+            boot_info.machtype == MACH_MIPS_MAGNUM_4000 ||              \
+            boot_info.machtype == MACH_OLIVETTI_M700)                   \
+		vdma_alloc(PHYSADDR(floppy_track_buffer),               \
+			   512*2*MAX_BUFFER_SECTORS);
+
+/*
+ * And on Mips's the CMOS info failes also ...
+ *
+ * FIXME: This information should come from the ARC configuration tree
+ *        or whereever a parivular machine has stored this ...
+ */
+#define FLOPPY0_TYPE 4		/* this is wrong for the Olli M700, but who cares... */
+#define FLOPPY1_TYPE 0
+
+#ifdef MODULE
+#define FDC1			((boot_info.machtype == MACH_ACER_PICA_61 || \
+				boot_info.machtype == MACH_MIPS_MAGNUM_4000 || \
+				boot_info.machtype == MACH_OLIVETTI_M700) ? \
+				0xe0003000 : 0x3f0)
+int FDC2=-1;
+#else
+#define FDC1			((boot_info.machtype == MACH_ACER_PICA_61 || \
+				boot_info.machtype == MACH_MIPS_MAGNUM_4000 || \
+				boot_info.machtype == MACH_OLIVETTI_M700) ? \
+				0xe0003000 : 0x3f0)
+static int FDC2=-1;
+#endif /* MODULE */
+
+#define N_FDC 1			/* do you *really* want a second controller? */
+#define N_DRIVE 8
+
+/*
+ * The DMA channel used by the floppy controller cannot access data at
+ * addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
+ *
+ * On MIPSes using vdma, this actually means that *all* transfers go thru
+ * the * track buffer since 0x1000000 is always smaller than KSEG0/1.
+ * Actually this needs to be a bit more complicated since the so much different
+ * hardware available with MIPS CPUs ...
+ */
+#define CROSS_64KB(a,s) ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64)
+
+#endif /* __ASM_MIPS_FLOPPY_H */
diff -u --recursive --new-file v1.3.35/linux/include/linux/blk.h linux/include/linux/blk.h
--- v1.3.35/linux/include/linux/blk.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/blk.h	Mon Oct 23 13:42:04 1995
@@ -0,0 +1,393 @@
+#ifndef _BLK_H
+#define _BLK_H
+
+#include <linux/blkdev.h>
+#include <linux/locks.h>
+#include <linux/config.h>
+
+/*
+ * NR_REQUEST is the number of entries in the request-queue.
+ * NOTE that writes may use only the low 2/3 of these: reads
+ * take precedence.
+ */
+#define NR_REQUEST	64
+
+/*
+ * This is used in the elevator algorithm: Note that
+ * reads always go before writes. This is natural: reads
+ * are much more time-critical than writes.
+ */
+#define IN_ORDER(s1,s2) \
+((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
+((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
+(s1)->sector < (s2)->sector)))))
+
+/*
+ * These will have to be changed to be aware of different buffer
+ * sizes etc.. It actually needs a major cleanup.
+ */
+#ifdef IDE_DRIVER
+#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
+#else
+#define SECTOR_MASK (blksize_size[MAJOR_NR] &&     \
+	blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] ? \
+	((blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] >> 9) - 1) :  \
+	((BLOCK_SIZE >> 9)  -  1))
+#endif /* IDE_DRIVER */
+
+#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
+
+#ifdef CONFIG_CDU31A
+extern int cdu31a_init(void);
+#endif CONFIG_CDU31A
+#ifdef CONFIG_MCD
+extern int mcd_init(void);
+#endif CONFIG_MCD
+#ifdef CONFIG_MCDX
+extern int mcdx_init(void);
+#endif CONFIG_MCDX
+#ifdef CONFIG_SBPCD
+extern int sbpcd_init(void);
+#endif CONFIG_SBPCD
+#ifdef CONFIG_AZTCD
+extern int aztcd_init(void);
+#endif CONFIG_AZTCD
+#ifdef CONFIG_CDU535
+extern int sony535_init(void);
+#endif CONFIG_CDU535
+#ifdef CONFIG_GSCD
+extern int gscd_init(void);
+#endif CONFIG_GSCD
+#ifdef CONFIG_CM206
+extern int cm206_init(void);
+#endif CONFIG_CM206
+#ifdef CONFIG_OPTCD
+extern int optcd_init(void);
+#endif CONFIG_OPTCD
+#ifdef CONFIG_SJCD
+extern int sjcd_init(void);
+#endif CONFIG_SJCD
+#ifdef CONFIG_BLK_DEV_HD
+extern int hd_init(void);
+#endif
+#ifdef CONFIG_BLK_DEV_IDE
+extern int ide_init(void);
+#endif
+#ifdef CONFIG_BLK_DEV_XD
+extern int xd_init(void);
+#endif
+
+extern void set_device_ro(kdev_t dev,int flag);
+
+extern int floppy_init(void);
+extern void rd_load(void);
+extern long rd_init(long mem_start, int length);
+extern int ramdisk_size;
+
+#define RO_IOCTLS(dev,where) \
+  case BLKROSET: if (!suser()) return -EACCES; \
+		 set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
+  case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
+		   if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
+		 
+#if defined(MAJOR_NR) || defined(IDE_DRIVER)
+
+/*
+ * Add entries as needed.
+ */
+
+#ifdef IDE_DRIVER
+
+#define DEVICE_NR(device)	(MINOR(device) >> PARTN_BITS)
+#define DEVICE_ON(device)	/* nothing */
+#define DEVICE_OFF(device)	/* nothing */
+
+#elif (MAJOR_NR == MEM_MAJOR)
+
+/* ram disk */
+#define DEVICE_NAME "ramdisk"
+#define DEVICE_REQUEST do_rd_request
+#define DEVICE_NR(device) (MINOR(device) & 7)
+#define DEVICE_ON(device) 
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == FLOPPY_MAJOR)
+
+static void floppy_off(unsigned int nr);
+
+#define DEVICE_NAME "floppy"
+#define DEVICE_INTR do_floppy
+#define DEVICE_REQUEST do_fd_request
+#define DEVICE_NR(device) ( (MINOR(device) & 3) | ((MINOR(device) & 0x80 ) >> 5 ))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
+
+#elif (MAJOR_NR == HD_MAJOR)
+
+/* harddisk: timeout is 6 seconds.. */
+#define DEVICE_NAME "harddisk"
+#define DEVICE_INTR do_hd
+#define DEVICE_TIMEOUT HD_TIMER
+#define TIMEOUT_VALUE (6*HZ)
+#define DEVICE_REQUEST do_hd_request
+#define DEVICE_NR(device) (MINOR(device)>>6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_DISK_MAJOR)
+
+#define DEVICE_NAME "scsidisk"
+#define DEVICE_INTR do_sd  
+#define TIMEOUT_VALUE (2*HZ)
+#define DEVICE_REQUEST do_sd_request
+#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
+
+#define DEVICE_NAME "scsitape"
+#define DEVICE_INTR do_st  
+#define DEVICE_NR(device) (MINOR(device) & 0x7f)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
+
+#define DEVICE_NAME "CD-ROM"
+#define DEVICE_INTR do_sr
+#define DEVICE_REQUEST do_sr_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == XT_DISK_MAJOR)
+
+#define DEVICE_NAME "xt disk"
+#define DEVICE_REQUEST do_xd_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
+
+#define DEVICE_NAME "CDU31A"
+#define DEVICE_REQUEST do_cdu31a_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
+
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcd */
+#define DEVICE_REQUEST do_mcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
+
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcdx */
+#define DEVICE_REQUEST do_mcdx_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #1"
+#define DEVICE_REQUEST do_sbpcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #2"
+#define DEVICE_REQUEST do_sbpcd2_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #3"
+#define DEVICE_REQUEST do_sbpcd3_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #4"
+#define DEVICE_REQUEST do_sbpcd4_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
+
+#define DEVICE_NAME "Aztech CD-ROM"
+#define DEVICE_REQUEST do_aztcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
+
+#define DEVICE_NAME "SONY-CDU535"
+#define DEVICE_INTR do_cdu535
+#define DEVICE_REQUEST do_cdu535_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
+
+#define DEVICE_NAME "Goldstar R420"
+#define DEVICE_REQUEST do_gscd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CM206_CDROM_MAJOR)
+#define DEVICE_NAME "Philips/LMS cd-rom cm206"
+#define DEVICE_REQUEST do_cm206_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
+
+#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
+#define DEVICE_REQUEST do_optcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
+
+#define DEVICE_NAME "Sanyo H94A CD-ROM"
+#define DEVICE_REQUEST do_sjcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#endif /* MAJOR_NR == whatever */
+
+#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
+
+#ifndef CURRENT
+#define CURRENT (blk_dev[MAJOR_NR].current_request)
+#endif
+
+#define CURRENT_DEV DEVICE_NR(CURRENT->rq_dev)
+
+#ifdef DEVICE_INTR
+void (*DEVICE_INTR)(void) = NULL;
+#endif
+#ifdef DEVICE_TIMEOUT
+
+#define SET_TIMER \
+((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
+(timer_active |= 1<<DEVICE_TIMEOUT))
+
+#define CLEAR_TIMER \
+timer_active &= ~(1<<DEVICE_TIMEOUT)
+
+#define SET_INTR(x) \
+if ((DEVICE_INTR = (x)) != NULL) \
+	SET_TIMER; \
+else \
+	CLEAR_TIMER;
+
+#else
+
+#define SET_INTR(x) (DEVICE_INTR = (x))
+
+#endif /* DEVICE_TIMEOUT */
+
+static void (DEVICE_REQUEST)(void);
+
+#ifdef DEVICE_INTR
+#define CLEAR_INTR SET_INTR(NULL)
+#else
+#define CLEAR_INTR
+#endif
+
+#define INIT_REQUEST \
+	if (!CURRENT) {\
+		CLEAR_INTR; \
+		return; \
+	} \
+	if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
+		panic(DEVICE_NAME ": request list destroyed"); \
+	if (CURRENT->bh) { \
+		if (!CURRENT->bh->b_lock) \
+			panic(DEVICE_NAME ": block not locked"); \
+	}
+
+#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) */
+
+/* end_request() - SCSI devices have their own version */
+/*               - IDE drivers have their own copy too */
+
+#if ! SCSI_MAJOR(MAJOR_NR)
+
+#if defined(_IDE_CD_C) || defined(_TRITON_C) /* shares copy with ide.c */
+void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
+#else
+
+#ifdef IDE_DRIVER
+void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) {
+	struct request *req = hwgroup->rq;
+#else
+static void end_request(int uptodate) {
+	struct request *req = CURRENT;
+#endif /* IDE_DRIVER */
+	struct buffer_head * bh;
+
+	req->errors = 0;
+	if (!uptodate) {
+		printk("end_request: I/O error, dev %s, sector %lu\n",
+			kdevname(req->rq_dev), req->sector);
+		req->nr_sectors--;
+		req->nr_sectors &= ~SECTOR_MASK;
+		req->sector += (BLOCK_SIZE / 512);
+		req->sector &= ~SECTOR_MASK;		
+	}
+
+	if ((bh = req->bh) != NULL) {
+		req->bh = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		bh->b_uptodate = uptodate;		
+		unlock_buffer(bh);
+		if ((bh = req->bh) != NULL) {
+			req->current_nr_sectors = bh->b_size >> 9;
+			if (req->nr_sectors < req->current_nr_sectors) {
+				req->nr_sectors = req->current_nr_sectors;
+				printk("end_request: buffer-list destroyed\n");
+			}
+			req->buffer = bh->b_data;
+			return;
+		}
+	}
+#ifdef IDE_DRIVER
+	hwgroup->rq = NULL;
+#else
+	DEVICE_OFF(req->rq_dev);
+	CURRENT = req->next;
+#endif /* IDE_DRIVER */
+	if (req->sem != NULL)
+		up(req->sem);
+	req->rq_status = RQ_INACTIVE;
+	wake_up(&wait_for_request);
+}
+#endif /* ndef _IDE_CD_C */
+#endif /* ! SCSI_MAJOR(MAJOR_NR) */
+
+#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
+
+#endif /* _BLK_H */
diff -u --recursive --new-file v1.3.35/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v1.3.35/linux/include/linux/cyclades.h	Sat Sep  9 15:26:53 1995
+++ linux/include/linux/cyclades.h	Fri Oct 20 10:10:39 1995
@@ -47,6 +47,9 @@
  * For definitions of the flags field, see tty.h
  */
 
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
 struct cyclades_port {
 	int                     magic;
 	int                     type;
diff -u --recursive --new-file v1.3.35/linux/include/linux/firewall.h linux/include/linux/firewall.h
--- v1.3.35/linux/include/linux/firewall.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/firewall.h	Tue Oct 17 13:42:35 1995
@@ -0,0 +1,40 @@
+#ifndef __LINUX_FIREWALL_H
+#define __LINUX_FIREWALL_H
+
+/*
+ *	Definitions for loadable firewall modules
+ */
+
+#define FW_BLOCK	0
+#define FW_ACCEPT	1
+#define FW_REJECT	(-1)
+#define FW_MASQUERADE	2
+#define FW_SKIP		3
+
+struct firewall_ops
+{
+	struct firewall_ops *next;
+	int (*fw_forward)(struct firewall_ops *this, int pf, 
+			struct sk_buff *skb, void *phdr);
+	int (*fw_input)(struct firewall_ops *this, int pf, 
+			struct sk_buff *skb, void *phdr);
+	int (*fw_output)(struct firewall_ops *this, int pf, 
+			struct sk_buff *skb, void *phdr);
+	/* Data falling in the second 486 cache line isn't used directly
+	   during a firewall call and scan, only by insert/delete and other
+	   unusual cases
+	 */
+	int fw_pf;		/* Protocol family 			*/	
+	int fw_priority;	/* Priority of chosen firewalls 	*/
+};
+
+#ifdef __KERNEL__
+extern int register_firewall(int pf, struct firewall_ops *fw);
+extern int unregister_firewall(int pf, struct firewall_ops *fw);
+extern int call_fw_firewall(int pf, struct sk_buff *skb, void *phdr);
+extern int call_in_firewall(int pf, struct sk_buff *skb, void *phdr);
+extern int call_out_firewall(int pf, struct sk_buff *skb, void *phdr);
+extern void fwchain_init(void);
+#endif
+
+#endif
diff -u --recursive --new-file v1.3.35/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.35/linux/include/linux/fs.h	Fri Oct 13 14:44:45 1995
+++ linux/include/linux/fs.h	Mon Oct 23 11:01:25 1995
@@ -102,9 +102,9 @@
 #define BLKRASET 4706 /* Set read ahead for block device */
 #define BLKRAGET 4707 /* get current read ahead setting */
 
-#define BMAP_IOCTL 1	/* obsolete - kept for compatibility */
-#define FIBMAP	   1	/* bmap access */
-#define FIGETBSZ   2	/* get the block size used for bmap */
+#define BMAP_IOCTL 1		/* obsolete - kept for compatibility */
+#define FIBMAP	   0x0001	/* bmap access */
+#define FIGETBSZ   0x0002	/* get the block size used for bmap */
 
 #ifdef __KERNEL__
 extern void buffer_init(void);
@@ -497,6 +497,7 @@
 extern void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buffer);
 extern int is_read_only(kdev_t dev);
 extern void brelse(struct buffer_head * buf);
+extern void bforget(struct buffer_head * buf);
 extern void set_blocksize(kdev_t dev, int size);
 extern struct buffer_head * bread(kdev_t dev, int block, int size);
 extern unsigned long bread_page(unsigned long addr,kdev_t dev,int b[],int size,int no_share);
diff -u --recursive --new-file v1.3.35/linux/include/linux/if_ppp.h linux/include/linux/if_ppp.h
--- v1.3.35/linux/include/linux/if_ppp.h	Tue Oct 10 18:46:38 1995
+++ linux/include/linux/if_ppp.h	Fri Oct 20 10:10:39 1995
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 3==
+ *  ==FILEVERSION 4==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -34,6 +34,12 @@
 
 #ifndef _IF_PPP_H_
 #define _IF_PPP_H_
+
+#if defined(__linux__)
+#include <linux/if.h>
+#include <linux/ioctl.h>
+#include <linux/ppp_defs.h>
+#endif
 
 /*
  * Packet sizes
diff -u --recursive --new-file v1.3.35/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v1.3.35/linux/include/linux/ip_fw.h	Tue Oct 10 18:46:38 1995
+++ linux/include/linux/ip_fw.h	Mon Oct 23 13:48:54 1995
@@ -164,10 +164,6 @@
 extern int ip_acct_ctl(int, void *, int);
 #endif
 
-#define FW_BLOCK	0
-#define FW_ACCEPT	1
-#define FW_REJECT	(-1)
-#define FW_MASQUERADE	2
 
 extern int ip_fw_chk(struct iphdr *, struct device *rif,struct ip_fw *, int, int);
 extern void ip_fw_init(void);
diff -u --recursive --new-file v1.3.35/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.3.35/linux/include/linux/mm.h	Fri Oct 13 14:44:45 1995
+++ linux/include/linux/mm.h	Mon Oct 23 13:41:22 1995
@@ -186,6 +186,7 @@
 extern int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma);
 extern int zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
 
+extern void vmtruncate(struct inode * inode, unsigned long offset);
 extern void handle_mm_fault(struct vm_area_struct *vma, unsigned long address, int write_access);
 extern void do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, int write_access);
 extern void do_no_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, int write_access);
diff -u --recursive --new-file v1.3.35/linux/include/linux/module.h linux/include/linux/module.h
--- v1.3.35/linux/include/linux/module.h	Tue Oct 10 18:46:38 1995
+++ linux/include/linux/module.h	Thu Oct 19 16:32:20 1995
@@ -90,12 +90,12 @@
  * define the count variable, and usage macros.
  */
 
-extern long mod_use_count_;
 #if defined(CONFIG_MODVERSIONS) && defined(MODULE) && !defined(__GENKSYMS__)
 int Using_Versions; /* gcc will handle this global (used as a flag) correctly */
 #endif
 
 #ifdef MODULE
+extern long mod_use_count_;
 #define MOD_INC_USE_COUNT      mod_use_count_++
 #define MOD_DEC_USE_COUNT      mod_use_count_--
 #define MOD_IN_USE	       (mod_use_count_ != 0)
diff -u --recursive --new-file v1.3.35/linux/include/linux/mroute.h linux/include/linux/mroute.h
--- v1.3.35/linux/include/linux/mroute.h	Wed Oct  4 14:14:34 1995
+++ linux/include/linux/mroute.h	Fri Oct 20 10:10:39 1995
@@ -1,6 +1,9 @@
 #ifndef __LINUX_MROUTE_H
 #define __LINUX_MROUTE_H
 
+#include <linux/sockios.h>
+#include <linux/in.h>
+
 /*
  *	Based on the MROUTING 3.5 defines primarily to keep
  *	source compatibility with BSD.
diff -u --recursive --new-file v1.3.35/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.3.35/linux/include/linux/sched.h	Fri Oct 13 14:44:45 1995
+++ linux/include/linux/sched.h	Mon Oct 23 13:41:22 1995
@@ -222,7 +222,8 @@
 	struct signal_struct *sig;
 #ifdef CONFIG_SMP
 	int processor;
-	int lock_depth;		/* Lock depth. We can context swithc in and out of holding a syscall kernel lock... */	
+	int last_processor;
+	int lock_depth;		/* Lock depth. We can context switch in and out of holding a syscall kernel lock... */	
 #endif	
 };
 
@@ -245,12 +246,14 @@
  */
 #define _STK_LIM	(8*1024*1024)
 
+#define DEF_PRIORITY	(15*HZ/100)	/* 150 ms time slices */
+
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
  */
 #define INIT_TASK \
-/* state etc */	{ 0,15*HZ/100,15*HZ/100,0,0,0,0, \
+/* state etc */	{ 0,DEF_PRIORITY,DEF_PRIORITY,0,0,0,0, \
 /* debugregs */ { 0, },            \
 /* exec domain */&default_exec_domain, \
 /* binfmt */	NULL, \
@@ -291,7 +294,7 @@
  *	On a single processor system this comes out as current_set[0] when cpp
  *	has finished with it, which gcc will optimise away.
  */
-#define current (current_set[smp_processor_id()])	/* Current on this processor */
+#define current (0+current_set[smp_processor_id()])	/* Current on this processor */
 extern unsigned long volatile jiffies;
 extern unsigned long itimer_ticks;
 extern unsigned long itimer_next;
diff -u --recursive --new-file v1.3.35/linux/include/net/ip.h linux/include/net/ip.h
--- v1.3.35/linux/include/net/ip.h	Wed Sep 27 16:00:00 1995
+++ linux/include/net/ip.h	Mon Oct 23 13:42:00 1995
@@ -48,46 +48,51 @@
  
 
 /* Describe an IP fragment. */
-struct ipfrag {
-  int		offset;		/* offset of fragment in IP datagram	*/
-  int		end;		/* last byte of data in datagram	*/
-  int		len;		/* length of this fragment		*/
-  struct sk_buff *skb;			/* complete received fragment		*/
-  unsigned char		*ptr;		/* pointer into real fragment data	*/
-  struct ipfrag		*next;		/* linked list pointers			*/
-  struct ipfrag		*prev;
+struct ipfrag 
+{
+	int		offset;		/* offset of fragment in IP datagram	*/
+	int		end;		/* last byte of data in datagram	*/
+	int		len;		/* length of this fragment		*/
+	struct sk_buff	*skb;		/* complete received fragment		*/
+	unsigned char	*ptr;		/* pointer into real fragment data	*/
+	struct ipfrag	*next;		/* linked list pointers			*/
+	struct ipfrag	*prev;
 };
 
-/* Describe an entry in the "incomplete datagrams" queue. */
-struct ipq	 {
-  unsigned char		*mac;		/* pointer to MAC header		*/
-  struct iphdr	*iph;		/* pointer to IP header			*/
-  int		len;		/* total length of original datagram	*/
-  short			ihlen;		/* length of the IP header		*/
-  short 	maclen;		/* length of the MAC header		*/
-  struct timer_list timer;	/* when will this queue expire?		*/
-  struct ipfrag		*fragments;	/* linked list of received fragments	*/
-  struct ipq	*next;		/* linked list pointers			*/
-  struct ipq	*prev;
-  struct device *dev;		/* Device - for icmp replies */
+/*
+ *	Describe an entry in the "incomplete datagrams" queue. 
+ */
+ 
+struct ipq	 
+{
+	unsigned char	*mac;		/* pointer to MAC header		*/
+	struct iphdr	*iph;		/* pointer to IP header			*/
+	int		len;		/* total length of original datagram	*/
+	short		ihlen;		/* length of the IP header		*/	
+	short 		maclen;		/* length of the MAC header		*/
+	struct timer_list timer;	/* when will this queue expire?		*/
+	struct ipfrag	*fragments;	/* linked list of received fragments	*/
+	struct ipq	*next;		/* linked list pointers			*/
+	struct ipq	*prev;
+	struct device	*dev;		/* Device - for icmp replies */
 };
 
+/*
+ *	Functions provided by ip.c
+ */
 
 extern void		ip_print(const struct iphdr *ip);
-extern int		ip_ioctl(struct sock *sk, int cmd,
-				 unsigned long arg);
-extern void		ip_route_check(__u32 daddr);
-extern int		ip_build_header(struct sk_buff *skb,
+extern int		ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
+extern void		ip_route_check(__u32 daddr); 
+extern int 		ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr);
+extern int 		ip_build_header(struct sk_buff *skb,
 					__u32 saddr,
 					__u32 daddr,
 					struct device **dev, int type,
 					struct options *opt, int len,
 					int tos,int ttl);
-/*extern unsigned short	ip_compute_csum(unsigned char * buff, int len);*/
 extern int		ip_rcv(struct sk_buff *skb, struct device *dev,
 			       struct packet_type *pt);
-extern int		ip_forward(struct sk_buff *skb, struct device *dev,
-				   int is_frag, __u32 target_addr);
 extern int		ip_options_echo(struct options * dopt, struct options * sopt,
 					__u32 daddr, __u32 saddr,
 					struct sk_buff * skb);
@@ -97,8 +102,6 @@
 extern void		ip_queue_xmit(struct sock *sk,
 				      struct device *dev, struct sk_buff *skb,
 				      int free);
-extern int 		ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
-extern int 		ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
 extern void		ip_init(void);
 extern int		ip_build_xmit(struct sock *sk,
 				      void getfrag (const void *,
@@ -116,4 +119,33 @@
 
 extern struct ip_mib	ip_statistics;
 
+/*
+ *	Functions provided by ip_fragment.o
+ */
+ 
+struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev);
+void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag);
+
+/*
+ *	Functions provided by ip_forward.c
+ */
+ 
+extern int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, __u32 target_addr);
+ 
+/*
+ *	Functions provided by ip_options.c
+ */
+ 
+extern void ip_options_build(struct sk_buff *skb, struct options *opt, __u32 daddr, __u32 saddr, int is_frag);
+extern int ip_options_echo(struct options *dopt, struct options *sopt, __u32 daddr, __u32 saddr, struct sk_buff *skb);
+extern void ip_options_fragment(struct sk_buff *skb);
+extern int ip_options_compile(struct options *opt, struct sk_buff *skb);
+
+/*
+ *	Functions provided by ip_sockglue.c
+ */
+
+extern int 		ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
+extern int 		ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
+  
 #endif	/* _IP_H */
diff -u --recursive --new-file v1.3.35/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.35/linux/include/net/sock.h	Wed Sep 13 12:45:33 1995
+++ linux/include/net/sock.h	Mon Oct 23 13:42:00 1995
@@ -77,26 +77,27 @@
  * Most of it is for TCP, and not used by any of
  * the other protocols.
  */
-struct sock {
-  struct options		*opt;
-  volatile unsigned long	wmem_alloc;
-  volatile unsigned long	rmem_alloc;
-  unsigned long			allocation;		/* Allocation mode */
-  __u32				write_seq;
-  __u32				sent_seq;
-  __u32				acked_seq;
-  __u32				copied_seq;
-  __u32				rcv_ack_seq;
-  __u32				window_seq;
-  __u32				fin_seq;
-  __u32				urg_seq;
-  __u32				urg_data;
+struct sock 
+{
+	struct options		*opt;
+	volatile unsigned long	wmem_alloc;
+	volatile unsigned long	rmem_alloc;
+	unsigned long		allocation;		/* Allocation mode */
+	__u32			write_seq;
+	__u32			sent_seq;
+	__u32			acked_seq;
+	__u32			copied_seq;
+	__u32			rcv_ack_seq;
+	__u32			window_seq;
+	__u32			fin_seq;
+	__u32			urg_seq;
+	__u32			urg_data;
 
   /*
-   * Not all are volatile, but some are, so we
-   * might as well say they all are.
+   *	Not all are volatile, but some are, so we
+   * 	might as well say they all are.
    */
-  volatile char                 inuse,
+	volatile char          	inuse,
 				dead,
 				urginline,
 				intr,
@@ -112,70 +113,76 @@
 				zapped,	/* In ax25 & ipx means not linked */
 				broadcast,
 				nonagle;
-  unsigned long		        lingertime;
-  int				proc;
-  struct sock			*next;
-  struct sock			*prev; /* Doubly linked chain.. */
-  struct sock			*pair;
-  struct sk_buff		* volatile send_head;
-  struct sk_buff		* volatile send_tail;
-  struct sk_buff_head		back_log;
-  struct sk_buff		*partial;
-  struct timer_list		partial_timer;
-  long				retransmits;
-  struct sk_buff_head		write_queue,
+	unsigned long	        lingertime;
+	int			proc;
+	struct sock		*next;
+	struct sock		*prev; /* Doubly linked chain.. */
+	struct sock		*pair;
+	struct sk_buff		* volatile send_head;
+	struct sk_buff		* volatile send_tail;
+	struct sk_buff_head	back_log;
+	struct sk_buff		*partial;
+	struct timer_list	partial_timer;
+	long			retransmits;
+	struct sk_buff_head	write_queue,
 				receive_queue;
-  struct proto			*prot;
-  struct wait_queue		**sleep;
-  __u32				daddr;
-  __u32				saddr;
-  unsigned short		max_unacked;
-  unsigned short		window;
-  unsigned short		bytes_rcv;
-/* mss is min(mtu, max_window) */
-  unsigned short		mtu;       /* mss negotiated in the syn's */
-  volatile unsigned short	mss;       /* current eff. mss - can change */
-  volatile unsigned short	user_mss;  /* mss requested by user in ioctl */
-  volatile unsigned short	max_window;
-  unsigned long 		window_clamp;
-  unsigned short		num;
-  volatile unsigned short	cong_window;
-  volatile unsigned short	cong_count;
-  volatile unsigned short	ssthresh;
-  volatile unsigned short	packets_out;
-  volatile unsigned short	shutdown;
-  volatile unsigned long	rtt;
-  volatile unsigned long	mdev;
-  volatile unsigned long	rto;
-/* currently backoff isn't used, but I'm maintaining it in case
- * we want to go back to a backoff formula that needs it
- */
-  volatile unsigned short	backoff;
-  volatile short		err;
-  unsigned char			protocol;
-  volatile unsigned char	state;
-  volatile unsigned char	ack_backlog;
-  unsigned char			max_ack_backlog;
-  unsigned char			priority;
-  unsigned char			debug;
-  unsigned short		rcvbuf;
-  unsigned short		sndbuf;
-  unsigned short		type;
-  unsigned char			localroute;	/* Route locally only */
+	struct proto		*prot;
+	struct wait_queue	**sleep;
+	__u32			daddr;
+	__u32			saddr;		/* Sending source */
+	__u32			rcv_saddr;	/* Bound address */
+	unsigned short		max_unacked;
+	unsigned short		window;
+	unsigned short		bytes_rcv;
+/*
+ *	mss is min(mtu, max_window) 
+ */
+	unsigned short		mtu;       /* mss negotiated in the syn's */
+	volatile unsigned short	mss;       /* current eff. mss - can change */
+	volatile unsigned short	user_mss;  /* mss requested by user in ioctl */
+	volatile unsigned short	max_window;
+	unsigned long 		window_clamp;
+	unsigned short		num;
+	volatile unsigned short	cong_window;
+	volatile unsigned short	cong_count;
+	volatile unsigned short	ssthresh;
+	volatile unsigned short	packets_out;
+	volatile unsigned short	shutdown;
+	volatile unsigned long	rtt;
+	volatile unsigned long	mdev;
+	volatile unsigned long	rto;
+
+/*
+ *	currently backoff isn't used, but I'm maintaining it in case
+ *	we want to go back to a backoff formula that needs it
+ */
+ 
+	volatile unsigned short	backoff;
+	volatile short		err;
+	unsigned char		protocol;
+	volatile unsigned char	state;
+	volatile unsigned char	ack_backlog;
+	unsigned char		max_ack_backlog;
+	unsigned char		priority;
+	unsigned char		debug;
+	unsigned short		rcvbuf;
+	unsigned short		sndbuf;
+	unsigned short		type;
+	unsigned char		localroute;	/* Route locally only */
 #ifdef CONFIG_IPX
-  ipx_address			ipx_dest_addr;
-  ipx_interface			*ipx_intrfc;
-  unsigned short		ipx_port;
-  unsigned short		ipx_type;
+/*
+ *	Once the IPX ncpd patches are in these are going into protinfo
+ */
+	ipx_address		ipx_dest_addr;
+	ipx_interface		*ipx_intrfc;
+	unsigned short		ipx_port;
+	unsigned short		ipx_type;
 #endif
 #ifdef CONFIG_AX25
-  ax25_cb			*ax25;
+	ax25_cb			*ax25;
 #ifdef CONFIG_NETROM
-  nr_cb				*nr;
-#endif
+	nr_cb			*nr;
 #endif
-#ifdef CONFIG_ATALK
-  struct atalk_sock		at;
 #endif
   
 /*
@@ -183,130 +190,148 @@
  *	overlap will eventually live. For now just AF_UNIX is here.
  */
 
-  union
-  {
-  	struct unix_opt		af_unix;
-  } protinfo;  		
-
-/* IP 'private area' or will be eventually */
-  int			ip_ttl;			/* TTL setting */
-  int			ip_tos;			/* TOS */
-  struct tcphdr		dummy_th;
-  struct timer_list	keepalive_timer;	/* TCP keepalive hack */
-  struct timer_list	retransmit_timer;	/* TCP retransmit timer */
-  struct timer_list	ack_timer;		/* TCP delayed ack timer */
-  int			ip_xmit_timeout;	/* Why the timeout is running */
-  struct rtable		*ip_route_cache;	/* Cached output route */
-  unsigned long		ip_route_stamp;		/* Route cache stamp */
-  unsigned long		ip_route_daddr;		/* Target address */
-  unsigned long		ip_route_saddr;		/* Source address */
-  int			ip_route_local;		/* State of locality flag */
-  unsigned long		ip_hcache_stamp;	/* Header cache stamp */
-  unsigned long 	*ip_hcache_ver;		/* Pointer to version of cache */
-  char			ip_hcache_data[16];	/* Cached header */
-  int			ip_hcache_state;	/* Have we a cached header */
-  unsigned char		ip_option_len;		/* Length of IP options */
-  unsigned char		ip_option_flen;		/* Second fragment option length */
-  unsigned char		ip_opt_next_strict;	/* Next hop is strict route */
-  unsigned long		ip_opt_next_hop;	/* Next hop if forced */
-  unsigned char 	*ip_opt_ptr[2];		/* IP option pointers */
-  unsigned char		ip_hdrincl;		/* Include headers ? */
+	union
+	{
+	  	struct unix_opt	af_unix;
+#ifdef CONFIG_ATALK
+		struct atalk_sock	af_at;
+#endif
+	} protinfo;  		
+
+/* 
+ *	IP 'private area' or will be eventually 
+ */
+	int			ip_ttl;			/* TTL setting */
+	int			ip_tos;			/* TOS */
+	struct tcphdr		dummy_th;
+	struct timer_list	keepalive_timer;	/* TCP keepalive hack */
+	struct timer_list	retransmit_timer;	/* TCP retransmit timer */
+	struct timer_list	ack_timer;		/* TCP delayed ack timer */
+	int			ip_xmit_timeout;	/* Why the timeout is running */
+	struct rtable		*ip_route_cache;	/* Cached output route */
+	unsigned long		ip_route_stamp;		/* Route cache stamp */
+	unsigned long		ip_route_daddr;		/* Target address */
+	unsigned long		ip_route_saddr;		/* Source address */
+	int			ip_route_local;		/* State of locality flag */
+	unsigned long		ip_hcache_stamp;	/* Header cache stamp */
+	unsigned long 		*ip_hcache_ver;		/* Pointer to version of cache */
+	char			ip_hcache_data[16];	/* Cached header */
+	int			ip_hcache_state;	/* Have we a cached header */
+	unsigned char		ip_option_len;		/* Length of IP options */
+	unsigned char		ip_option_flen;		/* Second fragment option length */
+	unsigned char		ip_opt_next_strict;	/* Next hop is strict route */
+	unsigned long		ip_opt_next_hop;	/* Next hop if forced */
+	unsigned char 		*ip_opt_ptr[2];		/* IP option pointers */
+	unsigned char		ip_hdrincl;		/* Include headers ? */
 #ifdef CONFIG_IP_MULTICAST  
-  int			ip_mc_ttl;		/* Multicasting TTL */
-  int			ip_mc_loop;		/* Loopback */
-  char			ip_mc_name[MAX_ADDR_LEN];/* Multicast device name */
-  struct ip_mc_socklist	*ip_mc_list;		/* Group array */
+	int			ip_mc_ttl;		/* Multicasting TTL */
+	int			ip_mc_loop;		/* Loopback */
+	char			ip_mc_name[MAX_ADDR_LEN];/* Multicast device name */
+	struct ip_mc_socklist	*ip_mc_list;		/* Group array */
 #endif  
 
-  /* This part is used for the timeout functions (timer.c). */
-  int			timeout;	/* What are we waiting for? */
-  struct timer_list	timer;		/* This is the TIME_WAIT/receive timer
+/*
+ *	This part is used for the timeout functions (timer.c). 
+ */
+ 
+	int			timeout;	/* What are we waiting for? */
+	struct timer_list	timer;		/* This is the TIME_WAIT/receive timer
 					 * when we are doing IP
 					 */
-  struct timeval	stamp;
+	struct timeval		stamp;
 
-  /* identd */
-  struct socket		*socket;
+ /*
+  *	Identd 
+  */
   
-  /* Callbacks */
-  void			(*state_change)(struct sock *sk);
-  void			(*data_ready)(struct sock *sk,int bytes);
-  void			(*write_space)(struct sock *sk);
-  void			(*error_report)(struct sock *sk);
+	struct socket		*socket;
+  
+  /*
+   *	Callbacks 
+   */
+   
+	void			(*state_change)(struct sock *sk);
+	void			(*data_ready)(struct sock *sk,int bytes);
+	void			(*write_space)(struct sock *sk);
+	void			(*error_report)(struct sock *sk);
   
 };
 
-struct proto {
-  struct sk_buff *	(*wmalloc)(struct sock *sk,
-				   unsigned long size, int force,
-				   int priority);
-  struct sk_buff *	(*rmalloc)(struct sock *sk,
-				   unsigned long size, int force,
-				   int priority);
-  void			(*wfree)(struct sock *sk, struct sk_buff *skb);
-  void			(*rfree)(struct sock *sk, struct sk_buff *skb);
-  unsigned long		(*rspace)(struct sock *sk);
-  unsigned long		(*wspace)(struct sock *sk);
-  void			(*close)(struct sock *sk, int timeout);
-  int			(*read)(struct sock *sk, unsigned char *to,
-				int len, int nonblock, unsigned flags);
-  int			(*write)(struct sock *sk, const unsigned char *to,
-				 int len, int nonblock, unsigned flags);
-  int			(*sendto)(struct sock *sk,
-				  const unsigned char *from, int len, 
-				  int noblock, unsigned flags,
-				  struct sockaddr_in *usin, int addr_len);
-  int			(*recvfrom)(struct sock *sk,
-				    unsigned char *from, int len, int noblock,
-				    unsigned flags, struct sockaddr_in *usin,
-				    int *addr_len);
-  int			(*build_header)(struct sk_buff *skb,
+/*
+ *	IP protocol blocks we attach to sockets.
+ */
+ 
+struct proto 
+{
+	void			(*close)(struct sock *sk, int timeout);
+	int			(*read)(struct sock *sk, unsigned char *to,
+					int len, int nonblock, unsigned flags);
+	int			(*write)(struct sock *sk, const unsigned char *to,
+				 	int len, int nonblock, unsigned flags);
+	int			(*sendto)(struct sock *sk,
+				  	const unsigned char *from, int len, 
+				  	int noblock, unsigned flags,
+				        struct sockaddr_in *usin, int addr_len);
+	int			(*recvfrom)(struct sock *sk,
+				        unsigned char *from, int len, int noblock,
+				        unsigned flags, struct sockaddr_in *usin,
+				        int *addr_len);
+	int			(*build_header)(struct sk_buff *skb,
 					__u32 saddr,
 					__u32 daddr,
 					struct device **dev, int type,
 					struct options *opt, int len,
 					int tos, int ttl);
-  int			(*connect)(struct sock *sk,
-				   struct sockaddr_in *usin, int addr_len);
-  struct sock *		(*accept) (struct sock *sk, int flags);
-  void			(*queue_xmit)(struct sock *sk,
-				      struct device *dev, struct sk_buff *skb,
-				      int free);
-  void			(*retransmit)(struct sock *sk, int all);
-  void			(*write_wakeup)(struct sock *sk);
-  void			(*read_wakeup)(struct sock *sk);
-  int			(*rcv)(struct sk_buff *buff, struct device *dev,
-			       struct options *opt, __u32 daddr,
-			       unsigned short len, __u32 saddr,
-			       int redo, struct inet_protocol *protocol);
-  int			(*select)(struct sock *sk, int which,
-				  select_table *wait);
-  int			(*ioctl)(struct sock *sk, int cmd,
-				 unsigned long arg);
-  int			(*init)(struct sock *sk);
-  void			(*shutdown)(struct sock *sk, int how);
-  int			(*setsockopt)(struct sock *sk, int level, int optname,
-				      char *optval, int optlen);
-  int			(*getsockopt)(struct sock *sk, int level, int optname,
-				      char *optval, int *option);  	 
-  unsigned short	max_header;
-  unsigned long		retransmits;
-  char			name[32];
-  int			inuse, highestinuse;
-  struct sock *		sock_array[SOCK_ARRAY_SIZE];
+	int			(*connect)(struct sock *sk,
+				        struct sockaddr_in *usin, int addr_len);
+	struct sock *		(*accept) (struct sock *sk, int flags);
+	void			(*queue_xmit)(struct sock *sk,
+				        struct device *dev, struct sk_buff *skb,
+				        int free);
+	void			(*retransmit)(struct sock *sk, int all);
+	void			(*write_wakeup)(struct sock *sk);
+	void			(*read_wakeup)(struct sock *sk);
+	int			(*rcv)(struct sk_buff *buff, struct device *dev,
+				        struct options *opt, __u32 daddr,
+				        unsigned short len, __u32 saddr,
+				        int redo, struct inet_protocol *protocol);
+	int			(*select)(struct sock *sk, int which,
+					select_table *wait);
+	int			(*ioctl)(struct sock *sk, int cmd,
+					unsigned long arg);
+	int			(*init)(struct sock *sk);
+	void			(*shutdown)(struct sock *sk, int how);
+	int			(*setsockopt)(struct sock *sk, int level, int optname,
+					char *optval, int optlen);
+	int			(*getsockopt)(struct sock *sk, int level, int optname,
+					char *optval, int *option);  	 
+	int			(*sendmsg)(struct sock *sk, struct msghdr *msg, int len,
+					int noblock, int flags);
+	int			(*recvmsg)(struct sock *sk, struct msghdr *msg, int len,
+					int noblock, int flags, int *addr_len);
+	unsigned short		max_header;
+	unsigned long		retransmits;
+	char			name[32];
+	int			inuse, highestinuse;
+	struct sock *		sock_array[SOCK_ARRAY_SIZE];
 };
 
 #define TIME_WRITE	1
 #define TIME_CLOSE	2
 #define TIME_KEEPOPEN	3
 #define TIME_DESTROY	4
-#define TIME_DONE	5	/* used to absorb those last few packets */
+#define TIME_DONE	5	/* Used to absorb those last few packets */
 #define TIME_PROBE0	6
-/* about 10 seconds */
+/*
+ *	About 10 seconds 
+ */
 #define SOCK_DESTROY_TIME (10*HZ)
 
 
-/* Sockets 0-1023 can't be bound too unless you are superuser */
+/*
+ *	Sockets 0-1023 can't be bound too unless you are superuser 
+ */
+ 
 #define PROT_SOCK	1024
 
 
@@ -380,7 +405,10 @@
 	return 0;
 }
 
-/* declarations from timer.c */
+/* 
+ *	Declarations from timer.c 
+ */
+ 
 extern struct sock *timer_base;
 
 extern void delete_timer (struct sock *);
@@ -388,7 +416,9 @@
 extern void net_timer (unsigned long);
 
 
-/* Enable debug/info messages */
+/* 
+ *	Enable debug/info messages 
+ */
 
 #define NETDEBUG(x)		x
 
diff -u --recursive --new-file v1.3.35/linux/init/main.c linux/init/main.c
--- v1.3.35/linux/init/main.c	Fri Oct 13 14:44:45 1995
+++ linux/init/main.c	Tue Oct 17 13:42:36 1995
@@ -460,9 +460,19 @@
 		current_set[i]=task[i];
 		current_set[i]->processor=i;
 	}
+}		
+
+/*
+ *	The autoprobe routines assume CPU#0 on the i386
+ *	so we don't actually set the game in motion until
+ *	they are finished.
+ */
+ 
+static void smp_begin(void)
+{
 	smp_threads_ready=1;
 	smp_commence();
-}		
+}
 	
 #endif
 
@@ -587,6 +597,15 @@
 	int pid,i;
 
 	setup();
+
+#ifdef CONFIG_SMP	
+	/*
+	 *	With the devices probed and setup we can
+	 *	now enter SMP mode.
+	 */
+	
+	smp_begin();
+#endif	
 
 	#ifdef CONFIG_UMSDOS_FS
 	{
diff -u --recursive --new-file v1.3.35/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.35/linux/kernel/ksyms.c	Fri Oct 13 14:44:45 1995
+++ linux/kernel/ksyms.c	Fri Oct 20 13:33:10 1995
@@ -41,6 +41,10 @@
 #include <linux/in.h>
 #include <linux/net.h>
 #include <linux/netdevice.h>
+#include <linux/firewall.h>
+#ifdef CONFIG_AX25
+#include <net/ax25.h>
+#endif
 #ifdef CONFIG_INET
 #include <linux/ip.h>
 #include <linux/etherdevice.h>
@@ -307,11 +311,16 @@
 	/* Miscellaneous access points */
 	X(si_meminfo),
 #ifdef CONFIG_NET
-	/* socket layer registration */
+	/* Socket layer registration */
 	X(sock_register),
 	X(sock_unregister),
-	/* Internet layer registration */
+#ifdef CONFIG_FIREWALL
+	/* Firewall registration */
+	X(register_firewall),
+	X(unregister_firewall),
+#endif
 #ifdef CONFIG_INET	
+	/* Internet layer registration */
 	X(inet_add_protocol),
 	X(inet_del_protocol),
 	X(rarp_ioctl_hook),
@@ -336,8 +345,12 @@
 	X(unregister_netdevice_notifier),
 #endif
 
-#ifdef CONFIG_INET
 	/* support for loadable net drivers */
+#ifdef CONFIG_AX25
+	X(ax25_encapsulate),
+	X(ax25_rebuild_header),	
+#endif	
+#ifdef CONFIG_INET
 	X(register_netdev),
 	X(unregister_netdev),
 	X(ether_setup),
diff -u --recursive --new-file v1.3.35/linux/kernel/sched.c linux/kernel/sched.c
--- v1.3.35/linux/kernel/sched.c	Wed Oct  4 14:14:34 1995
+++ linux/kernel/sched.c	Sun Oct 22 21:51:57 1995
@@ -167,6 +167,54 @@
 }
 
 /*
+ * This is the function that decides how desireable a process is..
+ * You can weigh different processes against each other depending
+ * on what CPU they've run on lately etc to try to handle cache
+ * and TLB miss penalties.
+ *
+ * Return values:
+ *	 -1000: never select this
+ *	     0: out of time, recalculate counters (but it might still be
+ *		selected)
+ *	   +ve: "goodness" value (the larger, the better)
+ *	 +1000: realtime process, select this.
+ */
+static inline int goodness(struct task_struct * p, int this_cpu)
+{
+	int weight;
+
+#ifdef CONFIG_SMP	
+	/* We are not permitted to run a task someone else is running */
+	if (p->processor != NO_PROC_ID)
+		return -1000;
+#endif
+
+	/*
+	 * Give the process a first-approximation goodness value
+	 * according to the number of clock-ticks it has left.
+	 *
+	 * Don't do any other calculations if the time slice is
+	 * over..
+	 */
+	weight = p->counter;
+	if (weight) {
+
+#ifdef CONFIG_SMP
+		/* Give a largish advantage to the same processor...   */
+		/* (this is equivalent to penalizing other processors) */
+		if (p->last_processor == this_cpu)
+			weight += PROC_CHANGE_PENALTY;
+#endif
+
+		/* .. and a slight advantage to the current process */
+		if (p == current)
+			weight += 1;
+	}
+
+	return weight;
+}
+
+/*
  *  'schedule()' is the scheduler function. It's a very simple and nice
  * scheduler: it's not perfect, but certainly works for most things.
  *
@@ -182,12 +230,7 @@
 	struct task_struct * p;
 	struct task_struct * next;
 	unsigned long timeout = 0;
-	
-#ifdef CONFIG_SMP_DEBUG
-	int proc=smp_processor_id();
-	if(active_kernel_processor!=proc)
-		panic("active kernel processor set wrongly! %d not %d\n", active_kernel_processor,proc);
-#endif
+	int this_cpu=smp_processor_id();
 
 /* check alarm, wake up any interruptible tasks that have got a signal */
 
@@ -234,15 +277,9 @@
 	c = -1000;
 	next = &init_task;
 	while (p != &init_task) {
-#ifdef CONFIG_SMP	
-		/* We are not permitted to run a task someone else is running */
-		if (p->processor != NO_PROC_ID) {
-			p = p->next_run;
-			continue;
-		}
-#endif		
-		if (p->counter > c)
-			c = p->counter, next = p;
+		int weight = goodness(p, this_cpu);
+		if (weight > c)
+			c = weight, next = p;
 		p = p->next_run;
 	}
 
@@ -262,7 +299,8 @@
 	 *	Allocate process to CPU
 	 */
 	 
-	 next->processor = smp_processor_id();
+	 next->processor = this_cpu;
+	 next->last_processor = this_cpu;
 	 
 #endif	 
 	if (current != next) {
@@ -554,6 +592,7 @@
 	break;
 
     case TIME_OOP:
+
 	time_state = TIME_WAIT;
 	break;
 
@@ -723,7 +762,7 @@
 	if (user_mode(regs)) {
 		current->utime++;
 		if (current->pid) {
-			if (current->priority < 15)
+			if (current->priority < DEF_PRIORITY)
 				kstat.cpu_nice++;
 			else
 				kstat.cpu_user++;
@@ -837,17 +876,36 @@
 	return current->egid;
 }
 
-asmlinkage int sys_nice(long increment)
+asmlinkage int sys_nice(int increment)
 {
-	int newprio;
+	unsigned long newprio;
+	int increase = 0;
 
-	if (increment < 0 && !suser())
-		return -EPERM;
+	newprio = increment;
+	if (increment < 0) {
+		if (!suser())
+			return -EPERM;
+		newprio = -increment;
+		increase = 1;
+	}
+	if (newprio > 40)
+		newprio = 40;
+	/*
+	 * do a "normalization" of the priority (traditionally
+	 * unix nice values are -20..20, linux doesn't really
+	 * use that kind of thing, but uses the length of the
+	 * timeslice instead (default 150 msec). The rounding is
+	 * why we want to avoid negative values.
+	 */
+	newprio = (newprio * DEF_PRIORITY + 10) / 20;
+	increment = newprio;
+	if (increase)
+		increment = -increment;
 	newprio = current->priority - increment;
 	if (newprio < 1)
 		newprio = 1;
-	if (newprio > 35)
-		newprio = 35;
+	if (newprio > DEF_PRIORITY*2)
+		newprio = DEF_PRIORITY*2;
 	current->priority = newprio;
 	return 0;
 }
diff -u --recursive --new-file v1.3.35/linux/kernel/sys.c linux/kernel/sys.c
--- v1.3.35/linux/kernel/sys.c	Fri Oct 13 14:44:45 1995
+++ linux/kernel/sys.c	Sat Oct 21 13:29:29 1995
@@ -58,13 +58,24 @@
 {
 	struct task_struct *p;
 	int error = ESRCH;
-	int priority;
+	unsigned int priority;
 
 	if (which > 2 || which < 0)
 		return -EINVAL;
 
-	if ((priority = PZERO - niceval) <= 0)
-		priority = 1;
+	/* normalize: avoid signed division (rounding problems) */
+	priority = niceval;
+	if (niceval < 0)
+		priority = -niceval;
+	if (priority > 20)
+		priority = 20;
+	priority = (priority * DEF_PRIORITY + 10) / 20 + DEF_PRIORITY;
+
+	if (niceval >= 0) {
+		priority = 2*DEF_PRIORITY - priority;
+		if (!priority)
+			priority = 1;
+	}
 
 	for_each_task(p) {
 		if (!proc_sel(p, which, who))
@@ -84,10 +95,15 @@
 	return -error;
 }
 
+/*
+ * Ugh. To avoid negative return values, "getpriority()" will
+ * not return the normal nice-value, but a value that has been
+ * offset by 20 (ie it returns 0..40 instead of -20..20)
+ */
 asmlinkage int sys_getpriority(int which, int who)
 {
 	struct task_struct *p;
-	int max_prio = -ESRCH;
+	long max_prio = -ESRCH;
 
 	if (which > 2 || which < 0)
 		return -EINVAL;
@@ -98,6 +114,10 @@
 		if (p->priority > max_prio)
 			max_prio = p->priority;
 	}
+
+	/* scale the priority from timeslice to 0..40 */
+	if (max_prio > 0)
+		max_prio = (max_prio * 20 + DEF_PRIORITY/2) / DEF_PRIORITY;
 	return max_prio;
 }
 
@@ -694,7 +714,7 @@
 	
 	if (!suser())
 		return -EPERM;
-	if (len > __NEW_UTS_LEN)
+	if (len < 0 || len > __NEW_UTS_LEN)
 		return -EINVAL;
 	error = verify_area(VERIFY_READ, name, len);
 	if (error)
diff -u --recursive --new-file v1.3.35/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.35/linux/mm/filemap.c	Mon Sep 25 12:26:23 1995
+++ linux/mm/filemap.c	Mon Oct 23 08:47:19 1995
@@ -48,14 +48,21 @@
 	} while (i > 0);
 }
 
+/*
+ * Semantics for shared and private memory areas are different past the end
+ * of the file. A shared mapping past the last page of the file is an error
+ * and results in a SIBGUS, while a private mapping just maps in a zero page.
+ */
 static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address,
 	unsigned long page, int no_share)
 {
 	struct inode * inode = area->vm_inode;
 	int nr[PAGE_SIZE/512];
 
-	multi_bmap(inode, (address & PAGE_MASK) - area->vm_start + area->vm_offset, nr,
-		inode->i_sb->s_blocksize_bits);
+	address = (address & PAGE_MASK) - area->vm_start + area->vm_offset;
+	if (address >= inode->i_size && (area->vm_flags & VM_SHARED) && area->vm_mm == current->mm)
+		send_sig(SIGBUS, current, 1);
+	multi_bmap(inode, address, nr, inode->i_sb->s_blocksize_bits);
 	return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, no_share);
 }
 
diff -u --recursive --new-file v1.3.35/linux/mm/memory.c linux/mm/memory.c
--- v1.3.35/linux/mm/memory.c	Mon Sep 25 12:26:23 1995
+++ linux/mm/memory.c	Mon Oct 23 09:09:40 1995
@@ -929,6 +929,114 @@
 }
 
 /*
+ * This function tries to find a page that is shared with the buffer cache,
+ * and if so it moves the buffer cache to a new location.
+ *
+ * It returns non-zero if we used up the "new_page" page.
+ */
+static int unshare(struct vm_area_struct *vma, unsigned long address, unsigned long new_page)
+{
+	pgd_t *page_dir;
+	pmd_t *page_middle;
+	pte_t *page_table, pte;
+	unsigned long old_page;
+	struct buffer_head * bh, * tmp;
+
+	page_dir = pgd_offset(vma->vm_mm, address);
+	if (pgd_none(*page_dir))
+		return 0;
+	if (pgd_bad(*page_dir)) {
+		printk("bad page table directory entry %p:[%lx]\n", page_dir, pgd_val(*page_dir));
+		pgd_clear(page_dir);
+		return 0;
+	}
+	page_middle = pmd_offset(page_dir, address);
+	if (pmd_none(*page_middle))
+		return 0;
+	if (pmd_bad(*page_middle)) {
+		printk("bad page table directory entry %p:[%lx]\n", page_dir, pgd_val(*page_dir));
+		pmd_clear(page_middle);
+		return 0;
+	}
+	page_table = pte_offset(page_middle, address);
+	pte = *page_table;
+	if (!pte_present(pte))
+		return 0;
+	old_page = pte_page(pte);
+	if (MAP_NR(old_page) > MAP_NR(high_memory))
+		return 0;
+	address &= ~PAGE_MASK;
+	memset((void *) (old_page + address), 0, PAGE_SIZE - address);
+	bh = buffer_pages[MAP_NR(old_page)];
+	if (!bh)
+		return 0;
+	if (!new_page) {
+		printk("Aieee... unshare(): no page available\n");
+		return 0;
+	}
+	buffer_pages[MAP_NR(old_page)] = NULL;
+	copy_page(old_page, new_page);
+	free_page(old_page);
+	old_page -= new_page;
+	buffer_pages[MAP_NR(new_page)] = bh;
+	tmp = bh;
+	do {
+		tmp->b_data -= old_page;
+		tmp = tmp->b_this_page;
+	} while (tmp != bh);
+	return 1;
+}
+
+/*
+ * Handle all mappings that got truncated by a "truncate()"
+ * system call.
+ *
+ * NOTE! We have to be ready to update the memory sharing
+ * between the file and the memory map for a potential last
+ * incomplete page.  Ugly, but necessary.
+ */
+void vmtruncate(struct inode * inode, unsigned long offset)
+{
+	unsigned long page;
+	struct vm_area_struct * mpnt;
+
+	if (!inode->i_mmap)
+		return;
+	page = __get_free_page(GFP_KERNEL);
+	mpnt = inode->i_mmap;
+	if (!mpnt) {
+		free_page(page);
+		return;
+	}
+	do {
+		unsigned long start = mpnt->vm_start;
+		unsigned long len = mpnt->vm_end - start;
+		unsigned long diff;
+
+		/* mapping wholly truncated? */
+		if (mpnt->vm_offset >= offset) {
+			zap_page_range(mpnt->vm_mm, start, len);
+			continue;
+		}
+		/* mapping wholly unaffected? */
+		diff = offset - mpnt->vm_offset;
+		if (diff >= len)
+			continue;
+		/* Ok, partially affected.. */
+		start += diff;
+		len = (len - diff) & PAGE_MASK;
+		/* Ugh, here comes the _really_ ugly part.. */
+		if (start & ~PAGE_MASK) {
+			if (unshare(mpnt, start, page))
+				page = 0;
+			start = (start + ~PAGE_MASK) & PAGE_MASK;
+		}
+		zap_page_range(mpnt->vm_mm, start, len);
+	} while ((mpnt = mpnt->vm_next_share) != inode->i_mmap);
+	free_page(page);
+}
+
+/*
  * fill in an empty page-table if none exists.
  */
 static inline pte_t * get_empty_pgtable(struct task_struct * tsk,unsigned long address)
diff -u --recursive --new-file v1.3.35/linux/net/Changes linux/net/Changes
--- v1.3.35/linux/net/Changes	Tue Oct 10 18:46:39 1995
+++ linux/net/Changes	Tue Oct 17 13:42:36 1995
@@ -250,9 +250,21 @@
 o	IP routing change errors resemble BSD more	[IN]
 o	IP port masquerading fixes			[IN]
 
+-------->>>>> 1.3.35 <<<<<<--------
+
+o	Appletalk data now in the protinfo union	[IN]
+o	BSD style bind to broadcast address supported	[IN]
+o	Standard loadable firewall chains		[IN]
+o	IPFW uses the firewall chains for firewall but
+		not yet acct/masquerade			[IN]
+o	Firewall chain hooks in all other protocols	[IN]
+o	Sendmsg/recvmsg for IPX. 			[IN]
+o	IPX uses sock_alloc_send_skb			[IN]
+o	Recvmsg for all IP, sendmsg for TCP		[IN]
+	(nearly ready to go all *msg())
+
 ---------- Things I thought Linus had for a while and not merged ----------------
 
-o	Paul Gortmakers 8390 Copy and checksum		[Pending]
 
 ---------- Things pending from other people to chase -------------
 
@@ -268,7 +280,6 @@
 
 o	inet_error for other layers
 o	Finish merging the bridge code
-o	SIOCSLEEPRT patch
 o	Fast checksum/copy on outgoing TCP
 o	Fast dev_grab_next() transmit reload function
 	and dev_push_failed() ??
@@ -306,7 +317,6 @@
 o	802.2 Class 2 services (eg netbios).
 o	Multidrop KISS					[NOW AVAILABLE IN USER MODE]
 o	Multicast routing				[STARTED BITS]
-o	IPX/Atalk/Netrom firewalling
 
 Possible projects for victim^H^H^H^H^Holunteers
 
@@ -374,7 +384,6 @@
 15.	802.2LLC and thus Netbeui sockets. Becoming less important since the
 rumour is microsoft are phasing out netbeui for netbios/IP. Microsoft have
 gone for netbios/funny-ipx-variant it seems in Win95, but TCP is selectable.
-	[Tentatively in progress]
 
 16.	X.25. This is one for a real head case with far too much time on 
 their hands. [Provisionally taken]
@@ -389,6 +398,8 @@
 
 20.	SKIP IP security using ENskip-0.10 - started
 [Me]
+
+21.	T/TCP support.
 
 BTW: Don't let the magic words 'kernel programming' worry you. Its like DOS
 - you make a mistake you have to reboot. You do at least get dumps and a
diff -u --recursive --new-file v1.3.35/linux/net/Config.in linux/net/Config.in
--- v1.3.35/linux/net/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/net/Config.in	Sun Oct 22 16:03:27 1995
@@ -0,0 +1,20 @@
+#
+# Network configuration
+#
+mainmenu_option next_comment
+comment 'Networking options'
+bool 'Network firewalls' CONFIG_FIREWALL
+bool 'TCP/IP networking' CONFIG_INET
+if [ "$CONFIG_INET" = "y" ]; then
+  source net/ipv4/Config.in
+fi
+bool 'The IPX protocol' CONFIG_IPX
+bool 'Appletalk DDP' CONFIG_ATALK
+bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
+if [ "$CONFIG_AX25" = "y" ]; then
+  bool 'Amateur Radio NET/ROM' CONFIG_NETROM
+fi
+bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK
+if [ "$CONFIG_NETLINK" = "y" ]; then
+  bool 'Routing messages' CONFIG_RTNETLINK
+fi
diff -u --recursive --new-file v1.3.35/linux/net/appletalk/aarp.c linux/net/appletalk/aarp.c
--- v1.3.35/linux/net/appletalk/aarp.c	Fri Aug 18 08:44:58 1995
+++ linux/net/appletalk/aarp.c	Tue Oct 17 13:42:36 1995
@@ -3,7 +3,7 @@
  *			ethernet 'ELAP'.
  *
  *		Alan Cox  <Alan.Cox@linux.org>
- *			  <iialan@www.linux.org.uk>
+ *			  <alan@cymru.net>
  *
  *	This doesn't fit cleanly with the IP arp. This isn't a problem as
  *	the IP arp wants extracting from the device layer in 1.3.x anyway.
@@ -152,7 +152,6 @@
 	 *	Send it.
 	 */	
 	 
-	 
 	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
 	
 	/*
@@ -435,6 +434,7 @@
 	/*
 	 *	Non ELAP we cannot do.
 	 */
+
 	if(dev->type!=ARPHRD_ETHER)
 	{
 		return -1;
@@ -466,6 +466,7 @@
 		/*
 		 *	Return 1 and fill in the address
 		 */
+
 		a->expires_at=jiffies+AARP_EXPIRY_TIME*10;
 		ddp_dl->datalink_header(ddp_dl, skb, a->hwaddr);
 		if(skb->sk==NULL)
@@ -475,22 +476,27 @@
 		restore_flags(flags);
 		return 1;
 	}
+
 	/*
 	 *	Do we have an unresolved entry: This is the less common path
 	 */
+
 	a=aarp_find_entry(unresolved[hash],dev,sa);
 	if(a!=NULL)
 	{
 		/*
 		 *	Queue onto the unresolved queue
 		 */
+
 		skb_queue_tail(&a->packet_queue, skb);
 		restore_flags(flags);
 		return 0;
 	}
+
 	/*
 	 *	Allocate a new entry
 	 */
+
 	a=aarp_alloc();
 	if(a==NULL)
 	{
@@ -501,9 +507,11 @@
 		restore_flags(flags);
 		return -1;
 	}
+
 	/*
 	 *	Set up the queue
 	 */
+
 	skb_queue_tail(&a->packet_queue, skb);
 	a->expires_at=jiffies+AARP_RESOLVE_TIME;
 	a->dev=dev;
@@ -513,26 +521,37 @@
 	unresolved[hash]=a;
 	unresolved_count++;
 	restore_flags(flags);
+
 	/*
 	 *	Send an initial request for the address
 	 */
+
 	aarp_send_query(a);
+
 	/*
 	 *	Switch to fast timer if needed (That is if this is the
 	 *	first unresolved entry to get added)
 	 */
+
 	if(unresolved_count==1)
 	{
 		del_timer(&aarp_timer);
 		aarp_timer.expires=jiffies+AARP_TICK_TIME;
 		add_timer(&aarp_timer);
 	}
+
 	/*
 	 *	Tell the ddp layer we have taken over for this frame.
 	 */
+
 	return 0;
 }
 
+/*
+ *	An entry in the aarp unresolved queue has become resolved. Send
+ *	all the frames queued under it.
+ */
+ 
 static void aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, int hash)
 {
 	struct sk_buff *skb;
@@ -542,10 +561,18 @@
 		{
 			unresolved_count--;
 			*list=a->next;
-			/* Move into the resolved list */
+			
+			/* 
+			 *	Move into the resolved list 
+			 */
+			 
 			a->next=resolved[hash];
 			resolved[hash]=a;
-			/* Kick frames off */
+			
+			/*
+			 *	Kick frames off 
+			 */
+			 
 			while((skb=skb_dequeue(&a->packet_queue))!=NULL)
 			{
 				a->expires_at=jiffies+AARP_EXPIRY_TIME*10;
@@ -561,6 +588,11 @@
 	}
 }
 
+/*
+ *	This is called by the SNAP driver whenever we see an AARP SNAP
+ *	frame. We currently only support ethernet.
+ */
+ 
 static int aarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct elapaarp *ea=(struct elapaarp *)skb->h.raw;
@@ -641,6 +673,7 @@
 			/*
 			 *	Fail the probe (in use)
 			 */
+			 
 			ifa->status|=ATIF_PROBE_FAIL;
 			restore_flags(flags);
 			kfree_skb(skb, FREE_READ);
@@ -663,6 +696,7 @@
 			/*
 			 *	We can fill one in - this is good
 			 */
+			 
 			memcpy(a->hwaddr,ea->hw_src,ETH_ALEN);
 			aarp_resolved(&unresolved[hash],a,hash);
 			if(unresolved_count==0)
@@ -696,6 +730,7 @@
 			/*
 			 *	aarp_my_address has found the address to use for us.
 			 */
+			 
 			aarp_send_reply(dev,ma,&sa,ea->hw_src);
 			break;
 	}
diff -u --recursive --new-file v1.3.35/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v1.3.35/linux/net/appletalk/ddp.c	Wed Sep 13 12:45:34 1995
+++ linux/net/appletalk/ddp.c	Tue Oct 17 13:42:36 1995
@@ -14,6 +14,9 @@
  *		Wesley Craig		:	Fix probing to listen to a
  *						passed node id.
  *		Alan Cox		:	Added send/recvmsg support
+ *		Alan Cox		:	Moved at. to protinfo in
+ *						socket.
+ *		Alan Cox		:	Added firewall hooks.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -54,6 +57,7 @@
 #include <linux/atalk.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <linux/firewall.h>
 
 #ifdef CONFIG_ATALK
 
@@ -126,23 +130,27 @@
 {
 	atalk_socket *s;
 
-	for( s = atalk_socket_list; s != NULL; s = s->next ) {
-	    if ( to->sat_port != s->at.src_port ) {
-		continue;
-	    }
+	for( s = atalk_socket_list; s != NULL; s = s->next ) 
+	{
+		if ( to->sat_port != s->protinfo.af_at.src_port ) 
+		{
+			continue;
+	   	}
 
-	    if ( to->sat_addr.s_net == 0 &&
+	    	if ( to->sat_addr.s_net == 0 &&
 		    to->sat_addr.s_node == ATADDR_BCAST &&
-		    s->at.src_net == atif->address.s_net ) {
-		break;
-	    }
+		    s->protinfo.af_at.src_net == atif->address.s_net ) 
+		{
+			break;
+	    	}
 
-	    if ( to->sat_addr.s_net == s->at.src_net &&
-		    to->sat_addr.s_node == s->at.src_node ) {
-		break;
-	    }
+	    	if ( to->sat_addr.s_net == s->protinfo.af_at.src_net &&
+		    to->sat_addr.s_node == s->protinfo.af_at.src_node ) 
+		{
+			break;
+	   	}
 
-	    /* XXXX.0 */
+	    	/* XXXX.0 */
 	}
 	return( s );
 }
@@ -155,17 +163,21 @@
 {
 	atalk_socket *s;
 
-	for ( s = atalk_socket_list; s != NULL; s = s->next ) {
-	    if ( s->at.src_net != sat->sat_addr.s_net ) {
-		continue;
-	    }
-	    if ( s->at.src_node != sat->sat_addr.s_node ) {
-		continue;
-	    }
-	    if ( s->at.src_port != sat->sat_port ) {
-		continue;
-	    }
-	    break;
+	for ( s = atalk_socket_list; s != NULL; s = s->next ) 
+	{
+		if ( s->protinfo.af_at.src_net != sat->sat_addr.s_net ) 
+		{
+			continue;
+		}
+		if ( s->protinfo.af_at.src_node != sat->sat_addr.s_node ) 
+		{
+			continue;
+		}
+		if ( s->protinfo.af_at.src_port != sat->sat_port ) 
+		{
+			continue;
+		}
+		break;
 	}
 	return( s );
 }
@@ -214,7 +226,10 @@
 }
 
 
-/* Called from proc fs */
+/*
+ *	Called from proc fs 
+ */
+ 
 int atalk_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	atalk_socket *s;
@@ -223,19 +238,17 @@
 	off_t begin=0;
 
 	/*
-	 *	Fill this in to print out the appletalk info you want
+	 *	Output the appletalk data for the /proc virtual fs.
 	 */
 
-	/* Theory.. Keep printing in the same place until we pass offset */
-	
 	len += sprintf (buffer,"Type local_addr  remote_addr tx_queue rx_queue st uid\n");
 	for (s = atalk_socket_list; s != NULL; s = s->next)
 	{
 		len += sprintf (buffer+len,"%02X   ", s->type);
 		len += sprintf (buffer+len,"%04X:%02X:%02X  ",
-			s->at.src_net,s->at.src_node,s->at.src_port);
+			s->protinfo.af_at.src_net,s->protinfo.af_at.src_node,s->protinfo.af_at.src_port);
 		len += sprintf (buffer+len,"%04X:%02X:%02X  ",
-			s->at.dest_net,s->at.dest_node,s->at.dest_port);
+			s->protinfo.af_at.dest_net,s->protinfo.af_at.dest_node,s->protinfo.af_at.dest_port);
 		len += sprintf (buffer+len,"%08lX:%08lX ", s->wmem_alloc, s->rmem_alloc);
 		len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
 		
@@ -432,13 +445,12 @@
 static struct atalk_iface *atalk_find_anynet(int node, struct device *dev)
 {
 	struct atalk_iface *iface;
-	for(iface=atalk_iface_list;iface!=NULL;iface=iface->next) {
-		if ( iface->dev != dev || ( iface->status & ATIF_PROBE )) {
+	for(iface=atalk_iface_list;iface!=NULL;iface=iface->next) 
+	{
+		if ( iface->dev != dev || ( iface->status & ATIF_PROBE ))
 			continue;
-		}
-		if ( node == ATADDR_BCAST || iface->address.s_node == node ) {
+		if ( node == ATADDR_BCAST || iface->address.s_node == node ) 
 			return iface;
-		}
 	}
 	return NULL;
 }
@@ -549,29 +561,30 @@
 		if(r->rt_flags != rt->flags)
 			continue;
 
-		if(ta->sat_addr.s_net == rt->target.s_net) {
-		    if(!(rt->flags&RTF_HOST))
-			break;
-		    if(ta->sat_addr.s_node == rt->target.s_node)
-			break;
+		if(ta->sat_addr.s_net == rt->target.s_net) 
+		{
+			if(!(rt->flags&RTF_HOST))
+				break;
+			if(ta->sat_addr.s_node == rt->target.s_node)
+				break;
 		}
 	}
 
-	if ( devhint == NULL ) {
-	    for ( riface = NULL, iface = atalk_iface_list; iface;
-		    iface = iface->next ) {
-		if ( riface == NULL && ntohs( ga->sat_addr.s_net ) >=
-			ntohs( iface->nets.nr_firstnet ) &&
-			ntohs( ga->sat_addr.s_net ) <=
-			ntohs( iface->nets.nr_lastnet ))
-		    riface = iface;
-		if ( ga->sat_addr.s_net == iface->address.s_net &&
-			ga->sat_addr.s_node == iface->address.s_node )
-		    riface = iface;
-	    }
-	    if ( riface == NULL )
-		return -ENETUNREACH;
-	    devhint = riface->dev;
+	if ( devhint == NULL ) 
+	{
+		for ( riface = NULL, iface = atalk_iface_list; iface; iface = iface->next ) 
+		{
+			if ( riface == NULL && ntohs( ga->sat_addr.s_net ) >= ntohs( iface->nets.nr_firstnet ) &&
+				ntohs( ga->sat_addr.s_net ) <=	ntohs( iface->nets.nr_lastnet ))
+			{
+				riface = iface;
+			}
+			if ( ga->sat_addr.s_net == iface->address.s_net && ga->sat_addr.s_node == iface->address.s_node )
+				riface = iface;
+		}
+		if ( riface == NULL )
+			return -ENETUNREACH;
+		devhint = riface->dev;
 	}
 
 	if(rt==NULL)
@@ -606,10 +619,12 @@
 	struct atalk_route **r = &atalk_router_list;
 	struct atalk_route *tmp;
 
-	while ((tmp = *r) != NULL) {
+	while ((tmp = *r) != NULL) 
+	{
 		if (tmp->target.s_net == addr->s_net &&
 			    (!(tmp->flags&RTF_GATEWAY) ||
-			    tmp->target.s_node == addr->s_node )) {
+			    tmp->target.s_node == addr->s_node )) 
+		{
 			*r = tmp->next;
 			kfree_s(tmp, sizeof(struct atalk_route));
 			return 0;
@@ -629,8 +644,10 @@
 	struct atalk_route **r = &atalk_router_list;
 	struct atalk_route *tmp;
 
-	while ((tmp = *r) != NULL) {
-		if (tmp->dev == dev) {
+	while ((tmp = *r) != NULL) 
+	{
+		if (tmp->dev == dev) 
+		{
 			*r = tmp->next;
 			kfree_s(tmp, sizeof(struct atalk_route));
 		}
@@ -751,11 +768,14 @@
 			/*
 			 *	Routerless initial state.
 			 */
-			if(nr->nr_firstnet==htons(0) && nr->nr_lastnet==htons(0xFFFE)) {
+			if(nr->nr_firstnet==htons(0) && nr->nr_lastnet==htons(0xFFFE)) 
+			{
 				sa->sat_addr.s_net=atif->address.s_net;
 				atrtr_create(&rtdef, dev);
 				atrtr_set_default(dev);
-			} else {
+			} 
+			else 
+			{
 				limit=ntohs(nr->nr_lastnet);
 				if(limit-ntohs(nr->nr_firstnet) > 256)
 				{
@@ -1093,13 +1113,13 @@
 	sk->type=sock->type;
 	sk->debug=0;
 	
-	sk->at.src_net=0;
-	sk->at.src_node=0;
-	sk->at.src_port=0;
-	
-	sk->at.dest_net=0;
-	sk->at.dest_node=0;
-	sk->at.dest_port=0;
+	sk->protinfo.af_at.src_net=0;
+	sk->protinfo.af_at.src_node=0;
+	sk->protinfo.af_at.src_port=0;
+	
+	sk->protinfo.af_at.dest_net=0;
+	sk->protinfo.af_at.dest_node=0;
+	sk->protinfo.af_at.dest_port=0;
 
 	sk->mtu=DDP_MAXSZ;
 	
@@ -1151,10 +1171,11 @@
  
 static int atalk_pick_port(struct sockaddr_at *sat)
 {
-	for ( sat->sat_port = ATPORT_RESERVED; sat->sat_port < ATPORT_LAST;
-		sat->sat_port++ )
-	    if ( atalk_find_socket( sat ) == NULL )
-		return sat->sat_port;
+	for ( sat->sat_port = ATPORT_RESERVED; sat->sat_port < ATPORT_LAST; sat->sat_port++ )
+	{
+		if ( atalk_find_socket( sat ) == NULL )
+			return sat->sat_port;
+	}
 	return -EBUSY;
 }
  		
@@ -1165,13 +1186,13 @@
 	int n;
 
 	if ( ap == NULL || ap->s_net == htons( ATADDR_ANYNET ))
-	    return -EADDRNOTAVAIL;
-	sk->at.src_net = sat.sat_addr.s_net = ap->s_net;
-	sk->at.src_node = sat.sat_addr.s_node = ap->s_node;
+		return -EADDRNOTAVAIL;
+	sk->protinfo.af_at.src_net = sat.sat_addr.s_net = ap->s_net;
+	sk->protinfo.af_at.src_node = sat.sat_addr.s_node = ap->s_node;
 
 	if (( n = atalk_pick_port( &sat )) < 0 )
-	    return( n );
-	sk->at.src_port=n;
+		return( n );
+	sk->protinfo.af_at.src_port=n;
 	atalk_insert_socket(sk);
 	sk->zapped=0;
 	return 0;
@@ -1202,16 +1223,15 @@
 		struct at_addr *ap=atalk_find_primary();
 		if(ap==NULL)
 			return -EADDRNOTAVAIL;
-		sk->at.src_net=addr->sat_addr.s_net=ap->s_net;
-		sk->at.src_node=addr->sat_addr.s_node=ap->s_node;
+		sk->protinfo.af_at.src_net=addr->sat_addr.s_net=ap->s_net;
+		sk->protinfo.af_at.src_node=addr->sat_addr.s_node=ap->s_node;
 	}
 	else
 	{			
-		if ( atalk_find_interface( addr->sat_addr.s_net,
-			addr->sat_addr.s_node ) == NULL )
-		    return -EADDRNOTAVAIL;
-		sk->at.src_net=addr->sat_addr.s_net;
-		sk->at.src_node=addr->sat_addr.s_node;
+		if ( atalk_find_interface( addr->sat_addr.s_net, addr->sat_addr.s_node ) == NULL )
+			return -EADDRNOTAVAIL;
+		sk->protinfo.af_at.src_net=addr->sat_addr.s_net;
+		sk->protinfo.af_at.src_node=addr->sat_addr.s_node;
 	}
 
 	if(addr->sat_port == ATADDR_ANYPORT)
@@ -1219,10 +1239,10 @@
 		int n = atalk_pick_port(addr);
 		if(n < 0)
 			return n;
-		sk->at.src_port=addr->sat_port=n;
+		sk->protinfo.af_at.src_port=addr->sat_port=n;
 	}
 	else
-		sk->at.src_port=addr->sat_port;
+		sk->protinfo.af_at.src_port=addr->sat_port;
 
 	if(atalk_find_socket(addr)!=NULL)
 		return -EADDRINUSE;	   
@@ -1264,9 +1284,9 @@
 	if(atrtr_get_dev(&addr->sat_addr)==NULL)
 		return -ENETUNREACH;
 		
-	sk->at.dest_port=addr->sat_port;
-	sk->at.dest_net=addr->sat_addr.s_net;
-	sk->at.dest_node=addr->sat_addr.s_node;
+	sk->protinfo.af_at.dest_port=addr->sat_port;
+	sk->protinfo.af_at.dest_net=addr->sat_addr.s_net;
+	sk->protinfo.af_at.dest_node=addr->sat_addr.s_node;
 	sock->state = SS_CONNECTED;
 	sk->state=TCP_ESTABLISHED;
 	return(0);
@@ -1316,15 +1336,15 @@
 	{
 		if(sk->state!=TCP_ESTABLISHED)
 			return -ENOTCONN;
-		sat.sat_addr.s_net=sk->at.dest_net;
-		sat.sat_addr.s_node=sk->at.dest_node;
-		sat.sat_port=sk->at.dest_port;
+		sat.sat_addr.s_net=sk->protinfo.af_at.dest_net;
+		sat.sat_addr.s_node=sk->protinfo.af_at.dest_node;
+		sat.sat_port=sk->protinfo.af_at.dest_port;
 	}
 	else
 	{
-		sat.sat_addr.s_net=sk->at.src_net;
-		sat.sat_addr.s_node=sk->at.src_node;
-		sat.sat_port=sk->at.src_port;
+		sat.sat_addr.s_net=sk->protinfo.af_at.src_net;
+		sat.sat_addr.s_node=sk->protinfo.af_at.src_node;
+		sat.sat_port=sk->protinfo.af_at.src_port;
 	}
 	sat.sat_family = AF_APPLETALK;
 	memcpy(uaddr,&sat,sizeof(sat));
@@ -1372,7 +1392,7 @@
 	/*
 	 *	Size check to see if ddp->deh_len was crap
 	 *	(Otherwise we'll detonate most spectacularly
-	 *	 in the middle of recvfrom()).
+	 *	 in the middle of recvmsg()).
 	 */
 	 
 	if(skb->len<sizeof(*ddp))
@@ -1392,6 +1412,16 @@
 		kfree_skb(skb,FREE_READ);
 		return(0);
 	}
+
+#ifdef CONFIG_FIREWALL
+	
+	if(call_in_firewall(AF_APPLETALK, skb, ddp)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	
+#endif	
 	
 	/* Check the packet is aimed at us */
 
@@ -1413,7 +1443,18 @@
 			kfree_skb(skb, FREE_READ);
 			return(0);
 		}
-
+		
+#ifdef CONFIG_FIREWALL		
+		/*
+		 *	Check firewall allows this routing
+		 */
+		
+		if(call_fw_firewall(AF_APPLETALK, skb, ddp)!=FW_ACCEPT)
+		{
+			kfree_skb(skb, FREE_READ);
+			return(0);
+		}
+#endif
 		ta.s_net=ddp->deh_dnet;
 		ta.s_node=ddp->deh_dnode;
 
@@ -1511,9 +1552,9 @@
 			return -ENOTCONN;
 		usat=&local_satalk;
 		usat->sat_family=AF_APPLETALK;
-		usat->sat_port=sk->at.dest_port;
-		usat->sat_addr.s_node=sk->at.dest_node;
-		usat->sat_addr.s_net=sk->at.dest_net;
+		usat->sat_port=sk->protinfo.af_at.dest_port;
+		usat->sat_addr.s_node=sk->protinfo.af_at.dest_node;
+		usat->sat_addr.s_net=sk->protinfo.af_at.dest_net;
 	}
 	
 	/* Build a packet */
@@ -1534,7 +1575,7 @@
 	{
 		struct at_addr at_hint;
 		at_hint.s_node=0;
-		at_hint.s_net=sk->at.src_net;
+		at_hint.s_net=sk->protinfo.af_at.src_net;
 		rt=atrtr_find(&at_hint);
 		if(rt==NULL)
 			return -ENETUNREACH;
@@ -1573,11 +1614,11 @@
 	*((__u16 *)ddp)=ntohs(*((__u16 *)ddp));
 
 	ddp->deh_dnet=usat->sat_addr.s_net;
-	ddp->deh_snet=sk->at.src_net;
+	ddp->deh_snet=sk->protinfo.af_at.src_net;
 	ddp->deh_dnode=usat->sat_addr.s_node;
-	ddp->deh_snode=sk->at.src_node;
+	ddp->deh_snode=sk->protinfo.af_at.src_node;
 	ddp->deh_dport=usat->sat_port;
-	ddp->deh_sport=sk->at.src_port;
+	ddp->deh_sport=sk->protinfo.af_at.src_port;
 
 	if(sk->debug)
 		printk("SK %p: Copy user data (%d bytes).\n", sk, len);
@@ -1588,6 +1629,16 @@
 		ddp->deh_sum=0;
 	else
 		ddp->deh_sum=atalk_checksum(ddp, len+sizeof(*ddp));
+		
+#ifdef CONFIG_FIREWALL
+
+	if(call_out_firewall(AF_APPLETALK, skb, ddp)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return -EPERM;
+	}	
+	
+#endif
 	
 	/*
 	 *	Loopback broadcast packets to non gateway targets (ie routes
@@ -1921,6 +1972,6 @@
 		atalk_if_get_info
 	});
 
-	printk("Appletalk BETA 0.13 for Linux NET3.031\n");
+	printk("Appletalk 0.14 for Linux NET3.032\n");
 }
 #endif
diff -u --recursive --new-file v1.3.35/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.35/linux/net/ax25/af_ax25.c	Wed Sep 27 16:00:01 1995
+++ linux/net/ax25/af_ax25.c	Tue Oct 17 13:42:36 1995
@@ -100,6 +100,7 @@
 #include <linux/notifier.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <linux/firewall.h>
 
 #include <net/ip.h>
 #include <net/arp.h>
@@ -1367,11 +1368,22 @@
 	/*
 	 *	Process the AX.25/LAPB frame.
 	 */
+	 
 	skb->h.raw = skb->data;
+	
+#ifdef CONFIG_FIREWALL
+	
+	if(call_in_firewall(PF_AX25, skb, skb->h.raw)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+#endif	
 
 	/*
 	 *	Parse the address header.
 	 */
+	 
 	if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type) == NULL) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -1386,7 +1398,8 @@
 	 *	Ours perhaps ?
 	 */
 	if (dp.lastrepeat + 1 < dp.ndigi) {		/* Not yet digipeated completely */
-		if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) {
+		if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) 
+		{
 			struct device *dev_out = dev;
 
 			/* We are the digipeater. Mark ourselves as repeated
@@ -1394,8 +1407,10 @@
 			dp.lastrepeat++;
 			dp.repeated[(int)dp.lastrepeat] = 1;
 
-			if (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) {
-				while (dp.lastrepeat + 1 < dp.ndigi) {
+			if (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) 
+			{
+				while (dp.lastrepeat + 1 < dp.ndigi) 
+				{
 					struct device *dev_scan;
 					if ((dev_scan = ax25rtr_get_dev(&dp.calls[dp.lastrepeat + 1])) == NULL)
 						break;
@@ -1411,6 +1426,13 @@
 				kfree_skb(skb, FREE_READ);
 
 			build_ax25_addr(skb->data, &src, &dest, &dp, type, MODULUS);
+#ifdef CONFIG_FIREWALL
+			if(call_fw_firewall(PF_AX25, skb,skb->data)!=FW_ACCEPT)
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+#endif						
 			skb->arp = 1;
 			ax25_queue_xmit(skb, dev_out, SOPRI_NORMAL);
 		} else {
@@ -2191,21 +2213,34 @@
 		ax25_cs_get_info
 	});
 
-	printk("G4KLX/GW4PTS AX.25 for Linux. Version 0.30 ALPHA for Linux NET3.031 (Linux 1.3.25)\n");
+	printk("G4KLX/GW4PTS AX.25 for Linux. Version 0.30 BETA for Linux NET3.032 (Linux 1.3.35)\n");
 }
 
 /*
  *	A small shim to dev_queue_xmit to handle the difference between
  *	KISS AX.25 and BPQ AX.25.
  */
+
 void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
 	static char bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 	unsigned char *ptr;
 	int size;
+	
+#ifdef CONFIG_FIREWALL
 
-	if (dev->type == ARPHRD_ETHER) {
-		if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {
+	if(call_out_firewall(PF_AX25, skb, skb->data)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return;
+	}	
+	
+#endif	
+
+	if (dev->type == ARPHRD_ETHER) 
+	{
+		if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) 
+		{
 			printk("ax25_queue_xmit: not enough space to add BPQ Ether header\n");
 			skb->free = 1;
 			kfree_skb(skb, FREE_WRITE);
@@ -2220,9 +2255,10 @@
 		*ptr++ = (size + 5) / 256;
 
 		dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
-	} else {
+	} 
+	else 
+	{
 		ptr = skb_push(skb, 1);
-		
 		*ptr++ = 0;			/* KISS */
 	}
 
@@ -2297,9 +2333,11 @@
   	if (arp_find(bp + 1, dest, dev, dev->pa_addr, skb))
   		return 1;
 
-	if (bp[16] == AX25_P_IP) {
+	if (bp[16] == AX25_P_IP) 
+	{
 		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
-		if (mode == 'V' || mode == 'v' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) {
+		if (mode == 'V' || mode == 'v' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) 
+		{
 			skb_device_unlock(skb);
 			skb_pull(skb, AX25_HEADER_LEN - 1);	/* Keep PID */
 			ax25_send_frame(skb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
diff -u --recursive --new-file v1.3.35/linux/net/core/Makefile linux/net/core/Makefile
--- v1.3.35/linux/net/core/Makefile	Mon Aug 28 14:52:25 1995
+++ linux/net/core/Makefile	Tue Oct 17 13:43:57 1995
@@ -9,10 +9,16 @@
 
 O_TARGET := core.o
 
-O_OBJS	:= sock.o skbuff.o iovec.o datagram.o
+O_OBJS	:= sock.o skbuff.o iovec.o datagram.o 
 
 ifdef CONFIG_NET
+
 O_OBJS	+= dev.o dev_mcast.o
+
+ifdef CONFIG_FIREWALL
+O_OBJS += firewall.o
+endif
+
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.35/linux/net/core/datagram.c linux/net/core/datagram.c
--- v1.3.35/linux/net/core/datagram.c	Mon Aug 28 14:52:25 1995
+++ linux/net/core/datagram.c	Tue Oct 17 13:44:40 1995
@@ -221,7 +221,7 @@
 				/* Connection still in progress */
 				break;
 			}
-			if (sk->prot && sk->prot->wspace(sk) >= MIN_WRITE_SPACE)
+			if (sk->prot && sock_wspace(sk) >= MIN_WRITE_SPACE)
 			{
 				return(1);
 			}
diff -u --recursive --new-file v1.3.35/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.35/linux/net/core/dev.c	Tue Oct 10 18:46:39 1995
+++ linux/net/core/dev.c	Tue Oct 17 13:44:40 1995
@@ -1370,4 +1370,3 @@
 	enable_bh(NET_BH);
 	return 0;
 }
-
diff -u --recursive --new-file v1.3.35/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
--- v1.3.35/linux/net/core/dev_mcast.c	Wed Sep 13 12:45:34 1995
+++ linux/net/core/dev_mcast.c	Tue Oct 17 13:44:40 1995
@@ -194,4 +194,3 @@
 	}
 	dev->mc_count=0;
 }
-
diff -u --recursive --new-file v1.3.35/linux/net/core/firewall.c linux/net/core/firewall.c
--- v1.3.35/linux/net/core/firewall.c	Thu Jan  1 02:00:00 1970
+++ linux/net/core/firewall.c	Wed Oct 18 08:04:06 1995
@@ -0,0 +1,157 @@
+/*
+ *	Generic loadable firewalls. At the moment only IP will actually
+ *	use these, but people can add the others as they are needed.
+ *
+ *	Authors:	Dave Bonn (for IP)
+ *	much hacked by:	Alan Cox
+ */
+ 
+#include <linux/skbuff.h>
+#include <linux/firewall.h>
+
+static int firewall_lock=0;
+static int firewall_policy[NPROTO];
+static struct firewall_ops *firewall_chain[NPROTO];
+
+/*
+ *	Register a firewall
+ */
+ 
+int register_firewall(int pf, struct firewall_ops *fw)
+{
+	struct firewall_ops **p;
+	
+	if(pf<0||pf>=NPROTO)
+		return -EINVAL;
+	if(pf!=PF_INET)
+		return -ENOPROTOOPT;
+	
+	/*
+	 *	Don't allow two people to adjust at once.
+	 */
+	 
+	while(firewall_lock)
+		schedule();
+	firewall_lock=1;
+	
+	p=&firewall_chain[pf];
+	
+	while(*p)
+	{
+		if(fw->fw_priority > (*p)->fw_priority)
+			break;
+		p=&((*p)->next);
+	}
+
+	fw->next=*p;
+	/*
+	 *	We need to set p atomically in case someone runs down the list
+	 *	at the wrong moment. This saves locking it 
+	 */
+	 
+	xchg(p,fw);
+
+	/*
+	 *	And release the sleep lock
+	 */
+
+	firewall_lock=0;
+	return 0;
+}
+
+/*
+ *	Unregister a firewall
+ */
+
+int unregister_firewall(int pf, struct firewall_ops *fw)
+{
+	struct firewall_ops **nl;
+	
+	if(pf<0||pf>=NPROTO)
+		return -EINVAL;
+	if(pf!=PF_INET)
+		return -ENOPROTOOPT;
+	
+	/*
+	 *	Don't allow two people to adjust at once.
+	 */
+	 
+	while(firewall_lock)
+		schedule();
+	firewall_lock=1;
+
+	nl=&firewall_chain[pf];
+	
+	while(*nl!=NULL)
+	{
+		if(*nl==fw)
+		{
+			struct firewall_ops *f=fw->next;
+			xchg(nl,f);
+			firewall_lock=0;
+			return 0;
+		}			
+		nl=&((*nl)->next);
+	}
+	firewall_lock=0;
+	return -ENOENT;
+}
+
+int call_fw_firewall(int pf, struct sk_buff *skb, void *phdr)
+{
+	struct firewall_ops *fw=firewall_chain[pf];
+	int result=firewall_policy[pf];
+	
+	while(fw!=NULL)
+	{
+		int rc=fw->fw_forward(fw,pf,skb,phdr);
+		if(rc!=FW_SKIP)
+			return rc;
+		fw=fw->next;
+	}
+	/* alan, is this right? */
+	return result;
+}
+
+/*
+ *	Actual invocation of the chains
+ */
+ 
+int call_in_firewall(int pf, struct sk_buff *skb, void *phdr)
+{
+	struct firewall_ops *fw=firewall_chain[pf];
+	int result=firewall_policy[pf];
+	
+	while(fw!=NULL)
+	{
+		int rc=fw->fw_input(fw,pf,skb,phdr);
+		if(rc!=FW_SKIP)
+			return rc;
+		fw=fw->next;
+	}
+	/* alan, is this right? */
+	return result;
+}
+
+int call_out_firewall(int pf, struct sk_buff *skb, void *phdr)
+{
+	struct firewall_ops *fw=firewall_chain[pf];
+	int result=firewall_policy[pf];
+	
+	while(fw!=NULL)
+	{
+		int rc=fw->fw_output(fw,pf,skb,phdr);
+		if(rc!=FW_SKIP)
+			return rc;
+		fw=fw->next;
+	}
+	/* alan, is this right? */
+	return result;
+}
+
+void fwchain_init(void)
+{
+	int i;
+	for(i=0;i<NPROTO;i++)
+		firewall_policy[i]=FW_ACCEPT;
+}
diff -u --recursive --new-file v1.3.35/linux/net/core/iovec.c linux/net/core/iovec.c
--- v1.3.35/linux/net/core/iovec.c	Sat Sep  9 15:26:53 1995
+++ linux/net/core/iovec.c	Tue Oct 17 13:44:40 1995
@@ -104,4 +104,3 @@
 		iov++;
 	}
 }
-
diff -u --recursive --new-file v1.3.35/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v1.3.35/linux/net/core/skbuff.c	Wed Sep 27 16:00:01 1995
+++ linux/net/core/skbuff.c	Tue Oct 17 13:44:40 1995
@@ -460,9 +460,9 @@
 	        if(skb->sk->prot!=NULL)
 		{
 			if (rw)
-		     		skb->sk->prot->rfree(skb->sk, skb);
+		     		sock_rfree(skb->sk, skb);
 		     	else
-		     		skb->sk->prot->wfree(skb->sk, skb);
+		     		sock_wfree(skb->sk, skb);
 
 		}
 		else
@@ -689,4 +689,3 @@
 {
 	return skb->lock? 1 : 0;
 }
-
diff -u --recursive --new-file v1.3.35/linux/net/core/sock.c linux/net/core/sock.c
--- v1.3.35/linux/net/core/sock.c	Wed Sep 27 16:00:01 1995
+++ linux/net/core/sock.c	Tue Oct 17 13:44:40 1995
@@ -569,5 +569,3 @@
 	}
 #endif  
 }
-
-
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v1.3.35/linux/net/ipv4/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/Config.in	Sun Oct 22 16:03:27 1995
@@ -0,0 +1,24 @@
+#
+# IP configuration
+#
+bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD
+bool 'IP: multicasting' CONFIG_IP_MULTICAST
+if [ "$CONFIG_FIREWALL" = "y" ]; then
+  bool 'IP: firewalling' CONFIG_IP_FIREWALL
+fi
+bool 'IP: accounting' CONFIG_IP_ACCT
+tristate 'IP: tunneling' CONFIG_NET_IPIP
+if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then
+  bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE
+  bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE
+fi
+if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_MULTICAST" = "y" ]; then
+  bool 'IP: multicast routing(in progress)' CONFIG_IP_MROUTE
+fi
+comment '(it is safe to leave these untouched)'
+bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
+tristate 'IP: Reverse ARP' CONFIG_INET_RARP
+bool 'IP: Assume subnets are local' CONFIG_INET_SNARL
+bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF
+bool 'IP: Drop source routed frames' CONFIG_IP_NOSR
+bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/Makefile linux/net/ipv4/Makefile
--- v1.3.35/linux/net/ipv4/Makefile	Wed Oct  4 14:14:35 1995
+++ linux/net/ipv4/Makefile	Tue Oct 17 13:42:36 1995
@@ -9,8 +9,9 @@
 
 O_TARGET := ipv4.o
 IPV4_OBJS	:= utils.o route.o proc.o timer.o protocol.o packet.o \
-		   arp.o ip.o raw.o icmp.o tcp.o udp.o devinet.o af_inet.o \
-		   igmp.o ip_fw.o ipmr.o
+		   arp.o ip_input.o ip_fragment.o ip_forward.o ip_options.o \
+		   ip_output.o ip_sockglue.o raw.o icmp.o tcp.o udp.o \
+		   devinet.o af_inet.o igmp.o ip_fw.o ipmr.o
 
 MOD_LIST_NAME := IPV4_MODULES
 M_OBJS :=
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.35/linux/net/ipv4/af_inet.c	Tue Oct 10 18:46:39 1995
+++ linux/net/ipv4/af_inet.c	Mon Oct 23 13:52:27 1995
@@ -31,7 +31,7 @@
  *		Alan Cox	:	bind() shouldn't abort existing but dead
  *					sockets. Stops FTP netin:.. I hope.
  *		Alan Cox	:	bind() works correctly for RAW sockets. Note
- *					that FreeBSD at least is broken in this respect
+ *					that FreeBSD at least was broken in this respect
  *					so be careful with compatibility tests...
  *		Alan Cox	:	routing cache support
  *		Alan Cox	:	memzero the socket structure for compactness.
@@ -44,6 +44,7 @@
  *		Alan Cox	:	BSD rather than common sense interpretation of
  *					listen.
  *		Germano Caronni	:	Assorted small races.
+ *		Alan Cox	:	sendmsg/recvmsg basic support.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -141,7 +142,10 @@
 		base += PROT_SOCK+(start % 1024);
 	}
 
-	/* Now look through the entire array and try to find an empty ptr. */
+	/*
+	 *	Now look through the entire array and try to find an empty ptr. 
+	 */
+	 
 	for(i=0; i < SOCK_ARRAY_SIZE; i++) 
 	{
 		j = 0;
@@ -178,8 +182,7 @@
 
 void put_sock(unsigned short num, struct sock *sk)
 {
-	struct sock *sk1;
-	struct sock *sk2;
+	struct sock **skp, *tmp;
 	int mask;
 	unsigned long flags;
 	
@@ -190,7 +193,10 @@
 	sk->next = NULL;
 	num = num &(SOCK_ARRAY_SIZE -1);
 
-	/* We can't have an interrupt re-enter here. */
+	/* 
+	 *	We can't have an interrupt re-enter here. 
+	 */
+	 
 	save_flags(flags);
 	cli();
 
@@ -204,40 +210,29 @@
 		restore_flags(flags);
 		return;
 	}
+	
 	restore_flags(flags);
 	for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) 
 	{
-		if ((mask & sk->saddr) &&
-		    (mask & sk->saddr) != (mask & 0xffffffff)) 
+		if ((mask & sk->rcv_saddr) &&
+		    (mask & sk->rcv_saddr) != (mask & 0xffffffff)) 
 		{
 			mask = mask << 8;
 			break;
 		}
 	}
+
+	/*
+	 * add the socket to the sock_array[]..
+	 */
+	skp = sk->prot->sock_array + num;
 	cli();
-	sk1 = sk->prot->sock_array[num];
-	for(sk2 = sk1; sk2 != NULL; sk2=sk2->next) 
-	{
-		if (!(sk2->saddr & mask)) 
-		{
-			if (sk2 == sk1) 
-			{
-				sk->next = sk->prot->sock_array[num];
-				sk->prot->sock_array[num] = sk;
-				sti();
-				return;
-			}
-			sk->next = sk2;
-			sk1->next= sk;
-			sti();
-			return;
-		}
-		sk1 = sk2;
+	while ((tmp = *skp) != NULL) {
+		if (!(tmp->rcv_saddr & mask))
+			break;
 	}
-
-	/* Goes at the end. */
-	sk->next = NULL;
-	sk1->next = sk;
+	sk->next = tmp;
+	*skp = sk;
 	sti();
 }
 
@@ -288,23 +283,37 @@
 
   	sk->inuse = 1;			/* just to be safe. */
 
-  	/* In case it's sleeping somewhere. */
+  	/* 
+  	 *	In case it's sleeping somewhere. 
+  	 */
+  	 
   	if (!sk->dead) 
   		sk->write_space(sk);
 
   	remove_sock(sk);
   
-  	/* Now we can no longer get new packets. */
+  	/*
+  	 *	Now we can no longer get new packets or once the
+  	 *	timers are killed, send them.
+  	 */
+  	 
   	delete_timer(sk);
-  	/* Nor send them */
 	del_timer(&sk->retransmit_timer);
 	
-	while ((skb = tcp_dequeue_partial(sk)) != NULL) {
+	/*
+	 *	Drain any partial frames
+	 */
+	 
+	while ((skb = tcp_dequeue_partial(sk)) != NULL) 
+	{
 		IS_SKB(skb);
 		kfree_skb(skb, FREE_WRITE);
 	}
 
-	/* Cleanup up the write buffer. */
+	/*
+	 *	Cleanup up the write buffer. 
+	 */
+	 
   	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		IS_SKB(skb);
 		kfree_skb(skb, FREE_WRITE);
@@ -334,7 +343,10 @@
 		}
 	}	
 
-	/* Now we need to clean up the send head. */
+	/*
+	 *	Now we need to clean up the send head. 
+	 */
+	 
 	cli();
 	for(skb = sk->send_head; skb != NULL; )
 	{
@@ -344,8 +356,8 @@
 		 * We need to remove skb from the transmit queue,
 		 * or maybe the arp queue.
 		 */
-		if (skb->next  && skb->prev) {
-/*			printk("destroy_sock: unlinked skb\n");*/
+		if (skb->next  && skb->prev) 
+		{
 			IS_SKB(skb);
 			skb_unlink(skb);
 		}
@@ -357,15 +369,20 @@
 	sk->send_head = NULL;
 	sti();
 
-  	/* And now the backlog. */
+  	/*
+  	 *	Now the backlog. 
+  	 */
+  	 
   	while((skb=skb_dequeue(&sk->back_log))!=NULL) 
   	{
 		/* this should [almost] never happen. */
-/*		printk("cleaning back_log\n");*/
 		kfree_skb(skb, FREE_READ);
 	}
 
-	/* Now if it has a half accepted/ closed socket. */
+	/*
+	 *	Now if it has a half accepted/ closed socket. 
+	 */
+	 
 	if (sk->pair) 
 	{
 		sk->pair->dead = 1;
@@ -681,7 +698,8 @@
 	sk->inuse = 0;
 	sk->delay_acks = 0;
 	sk->daddr = 0;
-	sk->saddr = 0 /* ip_my_addr() */;
+	sk->saddr = 0;
+	sk->rcv_saddr = 0;
 	sk->err = 0;
 	sk->next = NULL;
 	sk->pair = NULL;
@@ -778,7 +796,7 @@
 }
 
 /*
- * Return 1 if we still have things to send in our buffers.
+ *	Return 1 if we still have things to send in our buffers.
  */
  
 static inline int closing(struct sock * sk)
@@ -903,12 +921,25 @@
 	}
 	
 	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
-	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST)
+	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST)
 		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
-	  	
+
 	if (chk_addr_ret || addr->sin_addr.s_addr == 0)
-		sk->saddr = addr->sin_addr.s_addr;
-	
+	{
+		/*
+		 *      We keep a pair of addresses. rcv_saddr is the one
+		 *      used by get_sock_*(), and saddr is used for transmit.
+		 *
+		 *      In the BSD API these are the same except where it
+		 *      would be illegal to use (multicast/broadcast) in
+		 *      which case the sending device address is used.
+		 */
+		sk->rcv_saddr = addr->sin_addr.s_addr;
+		if(chk_addr_ret==IS_MULTICAST||chk_addr_ret==IS_BROADCAST)
+			sk->saddr = 0;  /* Use device */
+		else
+			sk->saddr = addr->sin_addr.s_addr;
+	}
 	if(sock->type != SOCK_RAW)
 	{
 		/* Make sure we are allowed to bind here. */
@@ -927,7 +958,7 @@
 			
 			if (sk2->num != snum) 
 				continue;		/* more than one */
-			if (sk2->saddr != sk->saddr) 
+			if (sk2->rcv_saddr != sk->rcv_saddr) 
 				continue;	/* socket per slot ! -FB */
 			if (!sk2->reuse || sk2->state==TCP_LISTEN) 
 			{
@@ -1148,11 +1179,14 @@
 	} 
 	else 
 	{
+		__u32 addr = sk->rcv_saddr;
+		if (!addr) {
+			addr = sk->saddr;
+			if (!addr)
+				addr = ip_my_addr();
+		}
 		sin->sin_port = sk->dummy_th.source;
-		if (sk->saddr == 0) 
-			sin->sin_addr.s_addr = ip_my_addr();
-		else 
-			sin->sin_addr.s_addr = sk->saddr;
+		sin->sin_addr.s_addr = addr;
 	}
 	*uaddr_len = sizeof(*sin);
 	return(0);
@@ -1179,6 +1213,21 @@
 			     (struct sockaddr_in*)sin, addr_len));
 }
 
+static int inet_recvmsg(struct socket *sock, struct msghdr *ubuf, int size, int noblock, 
+		   int flags, int *addr_len )
+{
+	struct sock *sk = (struct sock *) sock->data;
+	
+	if (sk->prot->recvmsg == NULL) 
+		return(-EOPNOTSUPP);
+	if(sk->err)
+		return inet_error(sk);
+	/* We may need to bind the socket. */
+	if(inet_autobind(sk)!=0)
+		return(-EAGAIN);
+	return(sk->prot->recvmsg(sk, ubuf, size, noblock, flags,addr_len));
+}
+
 
 static int inet_recv(struct socket *sock, void *ubuf, int size, int noblock,
 	  unsigned flags)
@@ -1241,6 +1290,26 @@
 			   (struct sockaddr_in *)sin, addr_len));
 }
 
+static int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, 
+	   int flags)
+{
+	struct sock *sk = (struct sock *) sock->data;
+	if (sk->shutdown & SEND_SHUTDOWN) 
+	{
+		send_sig(SIGPIPE, current, 1);
+		return(-EPIPE);
+	}
+	if (sk->prot->sendmsg == NULL) 
+		return(-EOPNOTSUPP);
+	if(sk->err)
+		return inet_error(sk);
+	/* We may need to bind the socket. */
+	if(inet_autobind(sk)!=0)
+		return -EAGAIN;
+	return(sk->prot->sendmsg(sk, msg, size, noblock, flags));
+			   
+}
+
 
 static int inet_shutdown(struct socket *sock, int how)
 {
@@ -1414,8 +1483,8 @@
 		if(s->dead && (s->state == TCP_CLOSE))
 			continue;
 		/* local address matches? */
-		if (s->saddr) {
-			if (s->saddr != laddr)
+		if (s->rcv_saddr) {
+			if (s->rcv_saddr != laddr)
 				continue;
 			score++;
 		}
@@ -1464,7 +1533,7 @@
 			continue;
 		if(s->daddr && s->daddr!=raddr)
 			continue;
- 		if(s->saddr  && s->saddr!=laddr)
+ 		if(s->rcv_saddr && s->rcv_saddr != laddr)
 			continue;
 		return(s);
   	}
@@ -1507,7 +1576,7 @@
 			continue;
 		if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) 
 			continue;
- 		if(s->saddr  && s->saddr!=laddr)
+ 		if(s->rcv_saddr  && s->rcv_saddr != laddr)
 			continue;
 		return(s);
   	}
@@ -1540,6 +1609,8 @@
 	inet_setsockopt,
 	inet_getsockopt,
 	inet_fcntl,
+	inet_sendmsg,
+	inet_recvmsg
 };
 
 extern unsigned long seq_offset;
@@ -1554,7 +1625,7 @@
 	int i;
 
 
-	printk("Swansea University Computer Society TCP/IP for NET3.031 (Snapshot #4)\n");
+	printk("Swansea University Computer Society TCP/IP for NET3.032\n");
 
 	/*
 	 *	Tell SOCKET that we are alive... 
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v1.3.35/linux/net/ipv4/icmp.c	Fri Oct 13 14:44:48 1995
+++ linux/net/ipv4/icmp.c	Tue Oct 17 13:48:52 1995
@@ -81,6 +81,7 @@
  *     of thing. (OK)
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -512,7 +513,7 @@
 	icmp_param.data_ptr=(icmph+1);
 	icmp_param.data_len=len;
 	if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
-	  icmp_build_xmit(&icmp_param, daddr, saddr);
+		icmp_build_xmit(&icmp_param, daddr, saddr);
 	kfree_skb(skb, FREE_READ);
 }
 
@@ -545,9 +546,9 @@
 	 */
 	 
 	{
-	  struct timeval tv;
-	  do_gettimeofday(&tv);
-	  times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+		struct timeval tv;
+		do_gettimeofday(&tv);
+		times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
 	}
 	times[2] = times[1];
 	memcpy((void *)&times[0], icmph+1, 4);		/* Incoming stamp */
@@ -557,7 +558,7 @@
 	icmp_param.data_ptr=&times;
 	icmp_param.data_len=12;
 	if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
-	  icmp_build_xmit(&icmp_param, daddr, saddr);
+		icmp_build_xmit(&icmp_param, daddr, saddr);
 	kfree_skb(skb,FREE_READ);
 }
 
@@ -586,7 +587,7 @@
 	icmp_param.data_ptr=&dev->pa_mask;
 	icmp_param.data_len=4;
 	if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
-	  icmp_build_xmit(&icmp_param, daddr, saddr);
+		icmp_build_xmit(&icmp_param, daddr, saddr);
 #endif	
 	kfree_skb(skb, FREE_READ);	
 }
@@ -711,8 +712,7 @@
 	icmp_socket.type=SOCK_RAW;
 	icmp_socket.ops=ops;
 	if((err=ops->create(&icmp_socket, IPPROTO_ICMP))<0)
-		panic("Failed to create the ICMP control socket (%d,%d,%p,%p).\n", -err,
-			current->euid, current, &init_task);
+		panic("Failed to create the ICMP control socket.\n");
 	sk=icmp_socket.data;
 	sk->allocation=GFP_ATOMIC;
 	sk->num = 256;			/* Don't receive any data */
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip.c linux/net/ipv4/ip.c
--- v1.3.35/linux/net/ipv4/ip.c	Tue Oct 10 18:46:39 1995
+++ linux/net/ipv4/ip.c	Thu Jan  1 02:00:00 1970
@@ -1,3655 +0,0 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		The Internet Protocol (IP) module.
- *
- * Version:	@(#)ip.c	1.0.16b	9/1/93
- *
- * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *		Donald Becker, <becker@super.org>
- *		Alan Cox, <gw4pts@gw4pts.ampr.org>
- *		Richard Underwood
- *		Stefan Becker, <stefanb@yello.ping.de>
- *		Jorge Cwik, <jorge@laser.satlink.net>
- *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
- *		
- *
- * Fixes:
- *		Alan Cox	:	Commented a couple of minor bits of surplus code
- *		Alan Cox	:	Undefining IP_FORWARD doesn't include the code
- *					(just stops a compiler warning).
- *		Alan Cox	:	Frames with >=MAX_ROUTE record routes, strict routes or loose routes
- *					are junked rather than corrupting things.
- *		Alan Cox	:	Frames to bad broadcast subnets are dumped
- *					We used to process them non broadcast and
- *					boy could that cause havoc.
- *		Alan Cox	:	ip_forward sets the free flag on the
- *					new frame it queues. Still crap because
- *					it copies the frame but at least it
- *					doesn't eat memory too.
- *		Alan Cox	:	Generic queue code and memory fixes.
- *		Fred Van Kempen :	IP fragment support (borrowed from NET2E)
- *		Gerhard Koerting:	Forward fragmented frames correctly.
- *		Gerhard Koerting: 	Fixes to my fix of the above 8-).
- *		Gerhard Koerting:	IP interface addressing fix.
- *		Linus Torvalds	:	More robustness checks
- *		Alan Cox	:	Even more checks: Still not as robust as it ought to be
- *		Alan Cox	:	Save IP header pointer for later
- *		Alan Cox	:	ip option setting
- *		Alan Cox	:	Use ip_tos/ip_ttl settings
- *		Alan Cox	:	Fragmentation bogosity removed
- *					(Thanks to Mark.Bush@prg.ox.ac.uk)
- *		Dmitry Gorodchanin :	Send of a raw packet crash fix.
- *		Alan Cox	:	Silly ip bug when an overlength
- *					fragment turns up. Now frees the
- *					queue.
- *		Linus Torvalds/ :	Memory leakage on fragmentation
- *		Alan Cox	:	handling.
- *		Gerhard Koerting:	Forwarding uses IP priority hints
- *		Teemu Rantanen	:	Fragment problems.
- *		Alan Cox	:	General cleanup, comments and reformat
- *		Alan Cox	:	SNMP statistics
- *		Alan Cox	:	BSD address rule semantics. Also see
- *					UDP as there is a nasty checksum issue
- *					if you do things the wrong way.
- *		Alan Cox	:	Always defrag, moved IP_FORWARD to the config.in file
- *		Alan Cox	: 	IP options adjust sk->priority.
- *		Pedro Roque	:	Fix mtu/length error in ip_forward.
- *		Alan Cox	:	Avoid ip_chk_addr when possible.
- *	Richard Underwood	:	IP multicasting.
- *		Alan Cox	:	Cleaned up multicast handlers.
- *		Alan Cox	:	RAW sockets demultiplex in the BSD style.
- *		Gunther Mayer	:	Fix the SNMP reporting typo
- *		Alan Cox	:	Always in group 224.0.0.1
- *	Pauline Middelink	:	Fast ip_checksum update when forwarding
- *					Masquerading support.
- *		Alan Cox	:	Multicast loopback error for 224.0.0.1
- *		Alan Cox	:	IP_MULTICAST_LOOP option.
- *		Alan Cox	:	Use notifiers.
- *		Bjorn Ekwall	:	Removed ip_csum (from slhc.c too)
- *		Bjorn Ekwall	:	Moved ip_fast_csum to ip.h (inline!)
- *		Stefan Becker   :       Send out ICMP HOST REDIRECT
- *	Arnt Gulbrandsen	:	ip_build_xmit
- *		Alan Cox	:	Per socket routing cache
- *		Alan Cox	:	Fixed routing cache, added header cache.
- *		Alan Cox	:	Loopback didnt work right in original ip_build_xmit - fixed it.
- *		Alan Cox	:	Only send ICMP_REDIRECT if src/dest are the same net.
- *		Alan Cox	:	Incoming IP option handling.
- *		Alan Cox	:	Set saddr on raw output frames as per BSD.
- *		Alan Cox	:	Stopped broadcast source route explosions.
- *		Alan Cox	:	Can disable source routing
- *		Takeshi Sone    :	Masquerading didn't work.
- *	Dave Bonn,Alan Cox	:	Faster IP forwarding whenever possible.
- *		Alan Cox	:	Memory leaks, tramples, misc debugging.
- *		Alan Cox	:	Fixed multicast (by popular demand 8))
- *		Alan Cox	:	Fixed forwarding (by even more popular demand 8))
- *		Alan Cox	:	Fixed SNMP statistics [I think]
- *	Gerhard Koerting	:	IP fragmentation forwarding fix
- *		Alan Cox	:	Device lock against page fault.
- *		Alan Cox	:	IP_HDRINCL facility.
- *	Werner Almesberger	:	Zero fragment bug
- *		Alan Cox	:	RAW IP frame length bug
- *		Alan Cox	:	Outgoing firewall on build_xmit
- *		A.N.Kuznetsov	:	IP_OPTIONS support throughout the kernel
- *		Alan Cox	:	Multicast routing hooks
- *
- *  
- *
- * To Fix:
- *		IP option processing is mostly not needed. ip_forward needs to know about routing rules
- *		and time stamp but that's about all. Use the route mtu field here too
- *		IP fragmentation wants rewriting cleanly. The RFC815 algorithm is much more efficient
- *		and could be made very efficient with the addition of some virtual memory hacks to permit
- *		the allocation of a buffer that can then be 'grown' by twiddling page tables.
- *		Output fragmentation wants updating along with the buffer management to use a single 
- *		interleaved copy algorithm so that fragmenting has a one copy overhead. Actual packet
- *		output should probably do its own fragmentation at the UDP/RAW layer. TCP shouldn't cause
- *		fragmentation anyway.
- *
- *		FIXME: copy frag 0 iph to qp->iph
- *
- *		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.
- */
-
-#include <asm/segment.h>
-#include <asm/system.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/config.h>
-
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-
-#include <net/snmp.h>
-#include <net/ip.h>
-#include <net/protocol.h>
-#include <net/route.h>
-#include <net/tcp.h>
-#include <net/udp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/arp.h>
-#include <net/icmp.h>
-#include <net/raw.h>
-#include <net/checksum.h>
-#include <linux/igmp.h>
-#include <linux/ip_fw.h>
-#include <linux/mroute.h>
-#include <net/netlink.h>
-
-#define CONFIG_IP_DEFRAG
-
-extern int last_retran;
-extern void sort_send(struct sock *sk);
-
-#define min(a,b)	((a)<(b)?(a):(b))
-
-/*
- *	SNMP management statistics
- */
-
-#ifdef CONFIG_IP_FORWARD
-struct ip_mib ip_statistics={1,64,};	/* Forwarding=Yes, Default TTL=64 */
-#else
-struct ip_mib ip_statistics={2,64,};	/* Forwarding=No, Default TTL=64 */
-#endif
-
-/* 
- * Write options to IP header, record destination address to
- * source route option, address of outgoing interface
- * (we should already know it, so that this  function is allowed be
- * called only after routing decision) and timestamp,
- * if we originate this datagram.
- */
-
-static void ip_options_build(struct sk_buff * skb, struct options * opt,
-			    __u32 daddr, __u32 saddr,
-			    int is_frag) {
-	unsigned char * iph = (unsigned char*)skb->ip_hdr;
-
-	memcpy(skb->proto_priv, opt, sizeof(struct options));
-	memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
-	opt = (struct options*)skb->proto_priv;
-	opt->is_data = 0;
-
-	if (opt->srr)
-	  memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
-
-	if (!is_frag) {
-		if (opt->rr_needaddr)
-		  memcpy(iph+opt->rr+iph[opt->rr+2]-5, &saddr, 4);
-		if (opt->ts_needaddr)
-		  memcpy(iph+opt->ts+iph[opt->ts+2]-9, &saddr, 4);
-		if (opt->ts_needtime) {
-			struct timeval tv;
-			__u32 midtime;
-			do_gettimeofday(&tv);
-			midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
-			memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
-		}
-		return;
-	}
-	if (opt->rr) {
-		memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
-		opt->rr = 0;
-		opt->rr_needaddr = 0;
-	}
-	if (opt->ts) {
-		memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
-		opt->ts = 0;
-		opt->ts_needaddr = opt->ts_needtime = 0;
-	}
-}
-
-int ip_options_echo(struct options * dopt, struct options * sopt,
-		     __u32 daddr, __u32 saddr,
-		     struct sk_buff * skb) {
-	unsigned char *sptr, *dptr;
-	int soffset, doffset;
-	int	optlen;
-
-	memset(dopt, 0, sizeof(struct options));
-
-	dopt->is_data = 1;
-
-	if (!sopt)
-	  sopt = (struct options*)skb->proto_priv;
-
-	if (sopt->optlen == 0) {
-		dopt->optlen = 0;
-		return 0;
-	}
-
-	sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) :
-		(unsigned char *)skb->ip_hdr);
-	dptr = dopt->__data;
-
-	if (sopt->rr) {
-		optlen  = sptr[sopt->rr+1];
-		soffset = sptr[sopt->rr+2];
-		dopt->rr = dopt->optlen + sizeof(struct iphdr);
-		memcpy(dptr, sptr+sopt->rr, optlen);
-		if (sopt->rr_needaddr && soffset <= optlen) {
-			if (soffset + 3 > optlen)
-			  return -EINVAL;
-			dptr[2] = soffset + 4;
-			dopt->rr_needaddr = 1;
-		}
-		dptr 	 += optlen;
-		dopt->optlen += optlen;
-	}
-	if (sopt->ts) {
-		optlen = sptr[sopt->ts+1];
-		soffset = sptr[sopt->ts+2];
-		dopt->ts = dopt->optlen + sizeof(struct iphdr);
-		memcpy(dptr, sptr+sopt->ts, optlen);
-		if (soffset <= optlen) {
-			if (dopt->ts_needaddr) {
-				if (soffset + 3 > optlen)
-				  return -EINVAL;
-				dopt->ts_needaddr = 1;
-				soffset += 4;
-			}
-			if (dopt->ts_needtime) {
-				if (soffset + 3 > optlen)
-				  return -EINVAL;
-				dopt->ts_needtime = 1;
-				soffset += 4;
-			}
-			if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) {
-				__u32 addr;
-				memcpy(&addr, sptr+soffset-9, 4);
-				if (ip_chk_addr(addr) == 0) {
-					dopt->ts_needtime = 0;
-					dopt->ts_needaddr = 0;
-					soffset -= 8;
-				}
-			}
-			dptr[2] = soffset;
-		}
-		dptr += optlen;
-		dopt->optlen += optlen;
-	}
-	if (sopt->srr) {
-		unsigned char * start = sptr+sopt->srr;
-		__u32 faddr;
-
-		optlen  = start[1];
-		soffset = start[2];
-		doffset = 0;
-		if (soffset > optlen)
-		  soffset = optlen + 1;
-		soffset -= 4;
-		if (soffset > 3) {
-			memcpy(&faddr, &start[soffset-1], 4);
-			for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
-			  memcpy(&dptr[doffset-1], &start[soffset-1], 4);
-			/*
-			 * RFC1812 requires to fix illegal source routes.
-			 */
-			if (memcmp(&saddr, &start[soffset+3], 4) == 0)
-			  doffset -= 4;
-		}
-		if (doffset > 3) {
-			memcpy(&start[doffset-1], &daddr, 4);
-			dopt->faddr = faddr;
-			dptr[0] = start[0];
-			dptr[1] = doffset+3;
-			dptr[2] = 4;
-			dptr += doffset+3;
-			dopt->srr = dopt->optlen + sizeof(struct iphdr);
-			dopt->optlen += doffset+3;
-			dopt->is_strictroute = sopt->is_strictroute;
-		}
-	}
-	while (dopt->optlen & 3) {
-		*dptr++ = IPOPT_END;
-		dopt->optlen++;
-	}
-	return 0;
-}
-
-static void ip_options_fragment(struct sk_buff * skb) {
-	unsigned char * optptr = (unsigned char*)skb->ip_hdr;
-	struct options * opt = (struct options*)skb->proto_priv;
-	int  l = opt->optlen;
-	int  optlen;
-
-	while (l > 0) {
-		switch (*optptr) {
-		      case IPOPT_END:
-			return;
-		      case IPOPT_NOOP:
-			l--;
-			optptr++;
-			continue;
-		}
-		optlen = optptr[1];
-		if (l<2 || optlen>l)
-		  return;
-		if (!(*optptr & 0x80))
-		  memset(optptr, IPOPT_NOOP, optlen);
-		l -= optlen;
-		optptr += optlen;
-	}
-	opt->ts = 0;
-	opt->rr = 0;
-	opt->rr_needaddr = 0;
-	opt->ts_needaddr = 0;
-	opt->ts_needtime = 0;
-	return;
-}
-
-/*
- * Verify options and fill pointers in struct optinos.
- * Caller should clear *opt, and set opt->data.
- * If opt == NULL, then skb->data should point to IP header.
- */
-
-int ip_options_compile(struct options * opt, struct sk_buff * skb)
-{
-	int l;
-	unsigned char * iph;
-	unsigned char * optptr;
-	int optlen;
-	unsigned char * pp_ptr = NULL;
-
-	if (!opt) {
-		opt = (struct options*)skb->proto_priv;
-		memset(opt, 0, sizeof(struct options));
-		iph = (unsigned char*)skb->ip_hdr;
-		opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
-		optptr = iph + sizeof(struct iphdr);
-		opt->is_data = 0;
-	} else {
-		optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1];
-		iph = optptr - sizeof(struct iphdr);
-	}
-
-	for (l = opt->optlen; l > 0; ) {
-		switch (*optptr) {
-		      case IPOPT_END:
-			for (optptr++, l--; l>0; l--) {
-				if (*optptr != IPOPT_END) {
-					*optptr = IPOPT_END;
-					opt->is_changed = 1;
-				}
-			}
-			goto eol;
-		      case IPOPT_NOOP:
-			l--;
-			optptr++;
-			continue;
-		}
-		optlen = optptr[1];
-		if (l<2 || optlen>l) {
-			pp_ptr = optptr;
-			break;
-		}
-		switch (*optptr) {
-		      case IPOPT_SSRR:
-		      case IPOPT_LSRR:
-			if (optlen < 3) {
-				pp_ptr = optptr + 1;
-				break;
-			}
-			if (optptr[2] < 4) {
-				pp_ptr = optptr + 2;
-				break;
-			}
-			/* NB: cf RFC-1812 5.2.4.1 */
-			if (opt->srr) {
-				pp_ptr = optptr;
-				break;
-			}
-			if (!skb) {
-				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
-					pp_ptr = optptr + 1;
-					break;
-				}
-				memcpy(&opt->faddr, &optptr[3], 4);
-				if (optlen > 7)
-				  memmove(&optptr[3], &optptr[7], optlen-7);
-			}
-			opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
-			opt->srr = optptr - iph;
-			break;
-		      case IPOPT_RR:
-			if (opt->rr) {
-				pp_ptr = optptr;
-				break;
-			}
-			if (optlen < 3) {
-				pp_ptr = optptr + 1;
-				break;
-			}
-			if (optptr[2] < 4) {
-				pp_ptr = optptr + 2;
-				break;
-			}
-			if (optptr[2] <= optlen) {
-				if (optptr[2]+3 > optlen) {
-					pp_ptr = optptr + 2;
-					break;
-				}
-				if (skb) {
-					memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4);
-					opt->is_changed = 1;
-				}
-				optptr[2] += 4;
-				opt->rr_needaddr = 1;
-			}
-			opt->rr = optptr - iph;
-			break;
-		      case IPOPT_TIMESTAMP:
-			if (opt->ts) {
-				pp_ptr = optptr;
-				break;
-			}
-			if (optlen < 4) {
-				pp_ptr = optptr + 1;
-				break;
-			}
-			if (optptr[2] < 5) {
-				pp_ptr = optptr + 2;
-				break;
-			}
-			if (optptr[2] <= optlen) {
-				struct timestamp * ts = (struct timestamp*)(optptr+1);
-				__u32 * timeptr = NULL;
-				if (ts->ptr+3 > ts->len) {
-					pp_ptr = optptr + 2;
-					break;
-				}
-				switch (ts->flags) {
-				      case IPOPT_TS_TSONLY:
-					opt->ts = optptr - iph;
-					if (skb) {
-						timeptr = (__u32*)&optptr[ts->ptr-1];
-						opt->is_changed = 1;
-					}
-					ts->ptr += 4;
-					break;
-				      case IPOPT_TS_TSANDADDR:
-					if (ts->ptr+7 > ts->len) {
-						pp_ptr = optptr + 2;
-						break;
-					}
-					opt->ts = optptr - iph;
-					if (skb) {
-						memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4);
-						timeptr = (__u32*)&optptr[ts->ptr+3];
-					}
-					opt->ts_needaddr = 1;
-					opt->ts_needtime = 1;
-					ts->ptr += 8;
-					break;
-				      case IPOPT_TS_PRESPEC:
-					if (ts->ptr+7 > ts->len) {
-						pp_ptr = optptr + 2;
-						break;
-					}
-					opt->ts = optptr - iph;
-					{
-						__u32 addr;
-						memcpy(&addr, &optptr[ts->ptr-1], 4);
-						if (ip_chk_addr(addr) == 0)
-						  break;
-						if (skb)
-						  timeptr = (__u32*)&optptr[ts->ptr+3];
-					}
-					opt->ts_needaddr = 1;
-					opt->ts_needtime = 1;
-					ts->ptr += 8;
-					break;
-				      default:
-					pp_ptr = optptr + 3;
-					break;
-				}
-				if (timeptr) {
-					struct timeval tv;
-					__u32  midtime;
-					do_gettimeofday(&tv);
-					midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
-					memcpy(timeptr, &midtime, sizeof(__u32));
-					opt->is_changed = 1;
-				}
-			} else {
-				struct timestamp * ts = (struct timestamp*)(optptr+1);
-				if (ts->overflow == 15) {
-					pp_ptr = optptr + 3;
-					break;
-				}
-				opt->ts = optptr - iph;
-				if (skb) {
-					ts->overflow++;
-					opt->is_changed = 1;
-				}
-			}
-			break;
-		      case IPOPT_SEC:
-		      case IPOPT_SID:
-		      default:
-			if (!skb) {
-				pp_ptr = optptr;
-				break;
-			}
-			break;
-		}
-		l -= optlen;
-		optptr += optlen;
-	}
-
-eol:
-	if (!pp_ptr)
-	  return 0;
-
-	if (skb) {
-		icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev);
-		kfree_skb(skb, FREE_READ);
-	}
-	return -EINVAL;
-}
-
-/*
- *	Handle the issuing of an ioctl() request
- *	for the ip device. This is scheduled to
- *	disappear
- */
-
-int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
-{
-	switch(cmd)
-	{
-		default:
-			return(-EINVAL);
-	}
-}
-
-
-/*
- *	Take an skb, and fill in the MAC header.
- */
-
-static int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
-{
-	int mac = 0;
-
-	skb->dev = dev;
-	skb->arp = 1;
-	if (dev->hard_header)
-	{
-		/*
-		 *	Build a hardware header. Source address is our mac, destination unknown
-		 *  	(rebuild header will sort this out)
-		 */
-		skb_reserve(skb,(dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
-		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
-		if (mac < 0)
-		{
-			mac = -mac;
-			skb->arp = 0;
-			skb->raddr = daddr;	/* next routing address */
-		}
-	}
-	return mac;
-}
-
-static int ip_send_room(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
-{
-	int mac = 0;
-
-	skb->dev = dev;
-	skb->arp = 1;
-	if (dev->hard_header)
-	{
-		skb_reserve(skb,MAX_HEADER);
-		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
-		if (mac < 0)
-		{
-			mac = -mac;
-			skb->arp = 0;
-			skb->raddr = daddr;	/* next routing address */
-		}
-	}
-	return mac;
-}
-
-int ip_id_count = 0;
-
-/*
- * This routine builds the appropriate hardware/IP headers for
- * the routine.  It assumes that if *dev != NULL then the
- * protocol knows what it's doing, otherwise it uses the
- * routing/ARP tables to select a device struct.
- */
-int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
-		struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
-{
-	struct rtable *rt;
-	__u32 raddr;
-	int tmp;
-	__u32 src;
-	struct iphdr *iph;
-	__u32 final_daddr = daddr;
-
-	if (opt && opt->srr)
-	  daddr = opt->faddr;
-
-	/*
-	 *	See if we need to look up the device.
-	 */
-
-#ifdef CONFIG_IP_MULTICAST	
-	if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name)
-		*dev=dev_get(skb->sk->ip_mc_name);
-#endif
-	if (*dev == NULL)
-	{
-		if(skb->localroute)
-			rt = ip_rt_local(daddr, NULL, &src);
-		else
-			rt = ip_rt_route(daddr, NULL, &src);
-		if (rt == NULL)
-		{
-			ip_statistics.IpOutNoRoutes++;
-			return(-ENETUNREACH);
-		}
-
-		*dev = rt->rt_dev;
-		/*
-		 *	If the frame is from us and going off machine it MUST MUST MUST
-		 *	have the output device ip address and never the loopback
-		 */
-		if (LOOPBACK(saddr) && !LOOPBACK(daddr))
-			saddr = src;/*rt->rt_dev->pa_addr;*/
-		raddr = rt->rt_gateway;
-
-	}
-	else
-	{
-		/*
-		 *	We still need the address of the first hop.
-		 */
-		if(skb->localroute)
-			rt = ip_rt_local(daddr, NULL, &src);
-		else
-			rt = ip_rt_route(daddr, NULL, &src);
-		/*
-		 *	If the frame is from us and going off machine it MUST MUST MUST
-		 *	have the output device ip address and never the loopback
-		 */
-		if (LOOPBACK(saddr) && !LOOPBACK(daddr))
-			saddr = src;/*rt->rt_dev->pa_addr;*/
-
-		raddr = (rt == NULL) ? 0 : rt->rt_gateway;
-	}
-
-	/*
-	 *	No source addr so make it our addr
-	 */
-	if (saddr == 0)
-		saddr = src;
-
-	/*
-	 *	No gateway so aim at the real destination
-	 */
-	if (raddr == 0)
-		raddr = daddr;
-
-	/*
-	 *	Now build the MAC header.
-	 */
-
-	if(type==IPPROTO_TCP)
-		tmp = ip_send_room(skb, raddr, len, *dev, saddr);
-	else
-		tmp = ip_send(skb, raddr, len, *dev, saddr);
-
-	/*
-	 *	Book keeping
-	 */
-
-	skb->dev = *dev;
-	skb->saddr = saddr;
-
-	/*
-	 *	Now build the IP header.
-	 */
-
-	/*
-	 *	If we are using IPPROTO_RAW, then we don't need an IP header, since
-	 *	one is being supplied to us by the user
-	 */
-
-	if(type == IPPROTO_RAW)
-		return (tmp);
-
-	/*
-	 *	Build the IP addresses
-	 */
-	 
-	if (opt)
-	  iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen);
-	else
-	  iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr));
-
-	iph->version  = 4;
-	iph->ihl      = 5;
-	iph->tos      = tos;
-	iph->frag_off = 0;
-	iph->ttl      = ttl;
-	iph->daddr    = daddr;
-	iph->saddr    = saddr;
-	iph->protocol = type;
-	skb->ip_hdr   = iph;
-
-	if (!opt || !opt->optlen)
-	  return sizeof(struct iphdr) + tmp;
-	if (opt->is_strictroute && rt && rt->rt_gateway) {
-	  ip_statistics.IpOutNoRoutes++;
-	  return -ENETUNREACH;
-	}
-	iph->ihl += opt->optlen>>2;
-	ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0);
-	return iph->ihl*4 + tmp;
-}
-
-
-/*
- *	Generate a checksum for an outgoing IP datagram.
- */
-
-void ip_send_check(struct iphdr *iph)
-{
-	iph->check = 0;
-	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-}
-
-
-/************************ Fragment Handlers From NET2E **********************************/
-
-
-/*
- *	This fragment handler is a bit of a heap. On the other hand it works quite
- *	happily and handles things quite well.
- */
-
-static struct ipq *ipqueue = NULL;		/* IP fragment queue	*/
-
-/*
- *	Create a new fragment entry.
- */
-
-static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
-{
-	struct ipfrag *fp;
-
-	fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
-	if (fp == NULL)
-	{
-		NETDEBUG(printk("IP: frag_create: no memory left !\n"));
-		return(NULL);
-	}
-	memset(fp, 0, sizeof(struct ipfrag));
-
-	/* Fill in the structure. */
-	fp->offset = offset;
-	fp->end = end;
-	fp->len = end - offset;
-	fp->skb = skb;
-	fp->ptr = ptr;
-
-	return(fp);
-}
-
-
-/*
- *	Find the correct entry in the "incomplete datagrams" queue for
- *	this IP datagram, and return the queue entry address if found.
- */
-
-static struct ipq *ip_find(struct iphdr *iph)
-{
-	struct ipq *qp;
-	struct ipq *qplast;
-
-	cli();
-	qplast = NULL;
-	for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next)
-	{
-		if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr &&
-			iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol)
-		{
-			del_timer(&qp->timer);	/* So it doesn't vanish on us. The timer will be reset anyway */
-			sti();
-			return(qp);
-		}
-	}
-	sti();
-	return(NULL);
-}
-
-
-/*
- *	Remove an entry from the "incomplete datagrams" queue, either
- *	because we completed, reassembled and processed it, or because
- *	it timed out.
- */
-
-static void ip_free(struct ipq *qp)
-{
-	struct ipfrag *fp;
-	struct ipfrag *xp;
-
-	/*
-	 * Stop the timer for this entry.
-	 */
-
-	del_timer(&qp->timer);
-
-	/* Remove this entry from the "incomplete datagrams" queue. */
-	cli();
-	if (qp->prev == NULL)
-	{
-		ipqueue = qp->next;
-		if (ipqueue != NULL)
-			ipqueue->prev = NULL;
-	}
-	else
-	{
-		qp->prev->next = qp->next;
-		if (qp->next != NULL)
-			qp->next->prev = qp->prev;
-	}
-
-	/* Release all fragment data. */
-
-	fp = qp->fragments;
-	while (fp != NULL)
-	{
-		xp = fp->next;
-		IS_SKB(fp->skb);
-		kfree_skb(fp->skb,FREE_READ);
-		kfree_s(fp, sizeof(struct ipfrag));
-		fp = xp;
-	}
-
-	/* Release the IP header. */
-	kfree_s(qp->iph, 64 + 8);
-
-	/* Finally, release the queue descriptor itself. */
-	kfree_s(qp, sizeof(struct ipq));
-	sti();
-}
-
-
-/*
- *	Oops- a fragment queue timed out.  Kill it and send an ICMP reply.
- */
-
-static void ip_expire(unsigned long arg)
-{
-	struct ipq *qp;
-
-	qp = (struct ipq *)arg;
-
-	/*
-	 *	Send an ICMP "Fragment Reassembly Timeout" message.
-	 */
-
-	ip_statistics.IpReasmTimeout++;
-	ip_statistics.IpReasmFails++;   
-	/* This if is always true... shrug */
-	if(qp->fragments!=NULL)
-		icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
-				ICMP_EXC_FRAGTIME, 0, qp->dev);
-
-	/*
-	 *	Nuke the fragment queue.
-	 */
-	ip_free(qp);
-}
-
-
-/*
- * 	Add an entry to the 'ipq' queue for a newly received IP datagram.
- * 	We will (hopefully :-) receive all other fragments of this datagram
- * 	in time, so we just create a queue for this datagram, in which we
- * 	will insert the received fragments at their respective positions.
- */
-
-static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
-{
-	struct ipq *qp;
-	int ihlen;
-
-	qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
-	if (qp == NULL)
-	{
-		NETDEBUG(printk("IP: create: no memory left !\n"));
-		return(NULL);
-		skb->dev = qp->dev;
-	}
-	memset(qp, 0, sizeof(struct ipq));
-
-	/*
-	 *	Allocate memory for the IP header (plus 8 octets for ICMP).
-	 */
-
-	ihlen = iph->ihl * 4;
-	qp->iph = (struct iphdr *) kmalloc(64 + 8, GFP_ATOMIC);
-	if (qp->iph == NULL)
-	{
-		NETDEBUG(printk("IP: create: no memory left !\n"));
-		kfree_s(qp, sizeof(struct ipq));
-		return(NULL);
-	}
-
-	memcpy(qp->iph, iph, ihlen + 8);
-	qp->len = 0;
-	qp->ihlen = ihlen;
-	qp->fragments = NULL;
-	qp->dev = dev;
-
-	/* Start a timer for this entry. */
-	qp->timer.expires = jiffies + IP_FRAG_TIME;	/* about 30 seconds	*/
-	qp->timer.data = (unsigned long) qp;		/* pointer to queue	*/
-	qp->timer.function = ip_expire;			/* expire function	*/
-	add_timer(&qp->timer);
-
-	/* Add this entry to the queue. */
-	qp->prev = NULL;
-	cli();
-	qp->next = ipqueue;
-	if (qp->next != NULL)
-		qp->next->prev = qp;
-	ipqueue = qp;
-	sti();
-	return(qp);
-}
-
-
-/*
- *	See if a fragment queue is complete.
- */
-
-static int ip_done(struct ipq *qp)
-{
-	struct ipfrag *fp;
-	int offset;
-
-	/* Only possible if we received the final fragment. */
-	if (qp->len == 0)
-		return(0);
-
-	/* Check all fragment offsets to see if they connect. */
-	fp = qp->fragments;
-	offset = 0;
-	while (fp != NULL)
-	{
-		if (fp->offset > offset)
-			return(0);	/* fragment(s) missing */
-		offset = fp->end;
-		fp = fp->next;
-	}
-
-	/* All fragments are present. */
-	return(1);
-}
-
-
-/*
- *	Build a new IP datagram from all its fragments.
- *
- *	FIXME: We copy here because we lack an effective way of handling lists
- *	of bits on input. Until the new skb data handling is in I'm not going
- *	to touch this with a bargepole. 
- */
-
-static struct sk_buff *ip_glue(struct ipq *qp)
-{
-	struct sk_buff *skb;
-	struct iphdr *iph;
-	struct ipfrag *fp;
-	unsigned char *ptr;
-	int count, len;
-
-	/*
-	 *	Allocate a new buffer for the datagram.
-	 */
-	len = qp->ihlen + qp->len;
-
-	if ((skb = dev_alloc_skb(len)) == NULL)
-	{
-		ip_statistics.IpReasmFails++;
-		NETDEBUG(printk("IP: queue_glue: no memory for gluing queue %p\n", qp));
-		ip_free(qp);
-		return(NULL);
-	}
-
-	/* Fill in the basic details. */
-	skb_put(skb,len);
-	skb->h.raw = skb->data;
-	skb->free = 1;
-
-	/* Copy the original IP headers into the new buffer. */
-	ptr = (unsigned char *) skb->h.raw;
-	memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
-	ptr += qp->ihlen;
-
-	count = 0;
-
-	/* Copy the data portions of all fragments into the new buffer. */
-	fp = qp->fragments;
-	while(fp != NULL)
-	{
-		if(count+fp->len > skb->len)
-		{
-			NETDEBUG(printk("Invalid fragment list: Fragment over size.\n"));
-			ip_free(qp);
-			kfree_skb(skb,FREE_WRITE);
-			ip_statistics.IpReasmFails++;
-			return NULL;
-		}
-		memcpy((ptr + fp->offset), fp->ptr, fp->len);
-		count += fp->len;
-		fp = fp->next;
-	}
-
-	/* We glued together all fragments, so remove the queue entry. */
-	ip_free(qp);
-
-	/* Done with all fragments. Fixup the new IP header. */
-	iph = skb->h.iph;
-	iph->frag_off = 0;
-	iph->tot_len = htons((iph->ihl * 4) + count);
-	skb->ip_hdr = iph;
-
-	ip_statistics.IpReasmOKs++;
-	return(skb);
-}
-
-
-/*
- *	Process an incoming IP datagram fragment.
- */
-
-static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
-{
-	struct ipfrag *prev, *next, *tmp;
-	struct ipfrag *tfp;
-	struct ipq *qp;
-	struct sk_buff *skb2;
-	unsigned char *ptr;
-	int flags, offset;
-	int i, ihl, end;
-
-	ip_statistics.IpReasmReqds++;
-
-	/* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
-	qp = ip_find(iph);
-
-	/* Is this a non-fragmented datagram? */
-	offset = ntohs(iph->frag_off);
-	flags = offset & ~IP_OFFSET;
-	offset &= IP_OFFSET;
-	if (((flags & IP_MF) == 0) && (offset == 0))
-	{
-		if (qp != NULL)
-			ip_free(qp);	/* Huh? How could this exist?? */
-		return(skb);
-	}
-
-	offset <<= 3;		/* offset is in 8-byte chunks */
-	ihl = iph->ihl * 4;
-
-	/*
-	 * If the queue already existed, keep restarting its timer as long
-	 * as we still are receiving fragments.  Otherwise, create a fresh
-	 * queue entry.
-	 */
-
-	if (qp != NULL)
-	{
-		/* ANK. If the first fragment is received,
-		 * we should remember the correct IP header (with options)
-		 */
-	        if (offset == 0)
-		{
-			qp->ihlen = ihl;
-			memcpy(qp->iph, iph, ihl+8);
-		}
-		del_timer(&qp->timer);
-		qp->timer.expires = jiffies + IP_FRAG_TIME;	/* about 30 seconds */
-		qp->timer.data = (unsigned long) qp;	/* pointer to queue */
-		qp->timer.function = ip_expire;		/* expire function */
-		add_timer(&qp->timer);
-	}
-	else
-	{
-		/*
-		 *	If we failed to create it, then discard the frame
-		 */
-		if ((qp = ip_create(skb, iph, dev)) == NULL)
-		{
-			skb->sk = NULL;
-			kfree_skb(skb, FREE_READ);
-			ip_statistics.IpReasmFails++;
-			return NULL;
-		}
-	}
-
-	/*
-	 *	Determine the position of this fragment.
-	 */
-
-	end = offset + ntohs(iph->tot_len) - ihl;
-
-	/*
-	 *	Point into the IP datagram 'data' part.
-	 */
-
-	ptr = skb->data + ihl;
-
-	/*
-	 *	Is this the final fragment?
-	 */
-
-	if ((flags & IP_MF) == 0)
-		qp->len = end;
-
-	/*
-	 * 	Find out which fragments are in front and at the back of us
-	 * 	in the chain of fragments so far.  We must know where to put
-	 * 	this fragment, right?
-	 */
-
-	prev = NULL;
-	for(next = qp->fragments; next != NULL; next = next->next)
-	{
-		if (next->offset > offset)
-			break;	/* bingo! */
-		prev = next;
-	}
-
-	/*
-	 * 	We found where to put this one.
-	 * 	Check for overlap with preceding fragment, and, if needed,
-	 * 	align things so that any overlaps are eliminated.
-	 */
-	if (prev != NULL && offset < prev->end)
-	{
-		i = prev->end - offset;
-		offset += i;	/* ptr into datagram */
-		ptr += i;	/* ptr into fragment data */
-	}
-
-	/*
-	 * Look for overlap with succeeding segments.
-	 * If we can merge fragments, do it.
-	 */
-
-	for(tmp=next; tmp != NULL; tmp = tfp)
-	{
-		tfp = tmp->next;
-		if (tmp->offset >= end)
-			break;		/* no overlaps at all */
-
-		i = end - next->offset;			/* overlap is 'i' bytes */
-		tmp->len -= i;				/* so reduce size of	*/
-		tmp->offset += i;			/* next fragment	*/
-		tmp->ptr += i;
-		/*
-		 *	If we get a frag size of <= 0, remove it and the packet
-		 *	that it goes with.
-		 */
-		if (tmp->len <= 0)
-		{
-			if (tmp->prev != NULL)
-				tmp->prev->next = tmp->next;
-			else
-				qp->fragments = tmp->next;
-
-			if (tfp->next != NULL)
-				tmp->next->prev = tmp->prev;
-			
-			next=tfp;	/* We have killed the original next frame */
-
-			kfree_skb(tmp->skb,FREE_READ);
-			kfree_s(tmp, sizeof(struct ipfrag));
-		}
-	}
-
-	/*
-	 *	Insert this fragment in the chain of fragments.
-	 */
-
-	tfp = NULL;
-	tfp = ip_frag_create(offset, end, skb, ptr);
-
-	/*
-	 *	No memory to save the fragment - so throw the lot
-	 */
-
-	if (!tfp)
-	{
-		skb->sk = NULL;
-		kfree_skb(skb, FREE_READ);
-		return NULL;
-	}
-	tfp->prev = prev;
-	tfp->next = next;
-	if (prev != NULL)
-		prev->next = tfp;
-	else
-		qp->fragments = tfp;
-
-	if (next != NULL)
-		next->prev = tfp;
-
-	/*
-	 * 	OK, so we inserted this new fragment into the chain.
-	 * 	Check if we now have a full IP datagram which we can
-	 * 	bump up to the IP layer...
-	 */
-
-	if (ip_done(qp))
-	{
-		skb2 = ip_glue(qp);		/* glue together the fragments */
-		return(skb2);
-	}
-	return(NULL);
-}
-
-
-/*
- *	This IP datagram is too large to be sent in one piece.  Break it up into
- *	smaller pieces (each of size equal to the MAC header plus IP header plus
- *	a block of the data of the original IP data part) that will yet fit in a
- *	single device frame, and queue such a frame for sending by calling the
- *	ip_queue_xmit().  Note that this is recursion, and bad things will happen
- *	if this function causes a loop...
- *
- *	Yes this is inefficient, feel free to submit a quicker one.
- *
- */
- 
-static void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
-{
-	struct iphdr *iph;
-	unsigned char *raw;
-	unsigned char *ptr;
-	struct sk_buff *skb2;
-	int left, mtu, hlen, len;
-	int offset;
-	unsigned long flags;
-
-	/*
-	 *	Point into the IP datagram header.
-	 */
-
-	raw = skb->data;
-#if 0
-	iph = (struct iphdr *) (raw + dev->hard_header_len);	
-	skb->ip_hdr = iph;
-#else
-	iph = skb->ip_hdr;
-#endif
-
-	/*
-	 *	Setup starting values.
-	 */
-
-	hlen = iph->ihl * 4;
-	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
-	hlen += dev->hard_header_len;		/* Total header size */
-	mtu = (dev->mtu - hlen);		/* Size of data space */
-	ptr = (raw + hlen);			/* Where to start from */
-
-	/*
-	 *	Check for any "DF" flag. [DF means do not fragment]
-	 */
-
-	if (ntohs(iph->frag_off) & IP_DF)
-	{
-		ip_statistics.IpFragFails++;
-		printk("ip_queue_xmit: frag needed\n");
-		return;
-	}
-
-	/*
-	 *	The protocol doesn't seem to say what to do in the case that the
-	 *	frame + options doesn't fit the mtu. As it used to fall down dead
-	 *	in this case we were fortunate it didn't happen
-	 */
-
-	if(mtu<8)
-	{
-		/* It's wrong but it's better than nothing */
-		icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev);
-		ip_statistics.IpFragFails++;
-		return;
-	}
-
-	/*
-	 *	Fragment the datagram.
-	 */
-
-	/*
-	 *	The initial offset is 0 for a complete frame. When
-	 *	fragmenting fragments it's wherever this one starts.
-	 */
-
-	if (is_frag & 2)
-		offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
-	else
-		offset = 0;
-
-
-	/*
-	 *	Keep copying data until we run out.
-	 */
-
-	while(left > 0)
-	{
-		len = left;
-		/* IF: it doesn't fit, use 'mtu' - the data space left */
-		if (len > mtu)
-			len = mtu;
-		/* IF: we are not sending upto and including the packet end
-		   then align the next start on an eight byte boundary */
-		if (len < left)
-		{
-			len/=8;
-			len*=8;
-		}
-		/*
-		 *	Allocate buffer.
-		 */
-
-		if ((skb2 = alloc_skb(len + hlen+15,GFP_ATOMIC)) == NULL)
-		{
-			NETDEBUG(printk("IP: frag: no memory for new fragment!\n"));
-			ip_statistics.IpFragFails++;
-			return;
-		}
-
-		/*
-		 *	Set up data on packet
-		 */
-
-		skb2->arp = skb->arp;
-		if(skb->free==0)
-			printk("IP fragmenter: BUG free!=1 in fragmenter\n");
-		skb2->free = 1;
-		skb_put(skb2,len + hlen);
-		skb2->h.raw=(char *) skb2->data;
-		/*
-		 *	Charge the memory for the fragment to any owner
-		 *	it might possess
-		 */
-
-		save_flags(flags);
-		if (sk)
-		{
-			cli();
-			sk->wmem_alloc += skb2->truesize;
-			skb2->sk=sk;
-		}
-		restore_flags(flags);
-		skb2->raddr = skb->raddr;	/* For rebuild_header - must be here */
-
-		/*
-		 *	Copy the packet header into the new buffer.
-		 */
-
-		memcpy(skb2->h.raw, raw, hlen);
-
-		/*
-		 *	Copy a block of the IP datagram.
-		 */
-		memcpy(skb2->h.raw + hlen, ptr, len);
-		left -= len;
-
-		skb2->h.raw+=dev->hard_header_len;
-
-		/*
-		 *	Fill in the new header fields.
-		 */
-		iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
-		iph->frag_off = htons((offset >> 3));
-		skb2->ip_hdr = iph;
-
-		/* ANK: dirty, but effective trick. Upgrade options only if
-		 * the segment to be fragmented was THE FIRST (otherwise,
-		 * options are already fixed) and make it ONCE
-		 * on the initial skb, so that all the following fragments
-		 * will inherit fixed options.
-		 */
-		if (offset == 0)
-		  ip_options_fragment(skb);
-
-		/*
-		 *	Added AC : If we are fragmenting a fragment thats not the
-		 *		   last fragment then keep MF on each bit
-		 */
-		if (left > 0 || (is_frag & 1))
-			iph->frag_off |= htons(IP_MF);
-		ptr += len;
-		offset += len;
-
-		/*
-		 *	Put this fragment into the sending queue.
-		 */
-
-		ip_statistics.IpFragCreates++;
-
-		ip_queue_xmit(sk, dev, skb2, 2);
-	}
-	ip_statistics.IpFragOKs++;
-}
-
-
-
-#ifdef CONFIG_IP_FORWARD
-#ifdef CONFIG_IP_MROUTE
-
-/*
- * 	Encapsulate a packet by attaching a valid IPIP header to it.
- *	This avoids tunnel drivers and other mess and gives us the speed so
- *	important for multicast video.
- */
- 
-static void ip_encap(struct sk_buff *skb, int len, struct device *out, __u32 daddr)
-{
-	/*
-	 *	There is space for the IPIP header and MAC left.
-	 *
-	 *	Firstly push down and install the IPIP header.
-	 */
-	struct iphdr *iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
-	if(len>65515)
-		len=65515;
-	iph->version	= 	4;
-	iph->tos	=	skb->ip_hdr->tos;
-	iph->ttl	=	skb->ip_hdr->ttl;
-	iph->frag_off	=	0;
-	iph->daddr	=	daddr;
-	iph->saddr	=	out->pa_addr;
-	iph->protocol	=	IPPROTO_IPIP;
-	iph->ihl	=	5;
-	iph->tot_len	=	htons(skb->len);
-	iph->id		=	htons(ip_id_count++);
-	ip_send_check(iph);
-
-	skb->dev = out;
-	skb->arp = 1;
-	skb->raddr=daddr;
-	/*
-	 *	Now add the physical header (driver will push it down).
-	 */
-	if (out->hard_header && out->hard_header(skb, out, ETH_P_IP, NULL, NULL, len)<0)
-			skb->arp=0;
-	/*
-	 *	Read to queue for transmission.
-	 */
-}
-
-#endif
-
-/*
- *	Forward an IP datagram to its next destination.
- */
-
-int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
-	       __u32 target_addr)
-{
-	struct device *dev2;	/* Output device */
-	struct iphdr *iph;	/* Our header */
-	struct sk_buff *skb2;	/* Output packet */
-	struct rtable *rt;	/* Route we use */
-	unsigned char *ptr;	/* Data pointer */
-	unsigned long raddr;	/* Router IP address */
-	struct   options * opt	= (struct options*)skb->proto_priv;
-	int encap = 0;		/* Encap length */
-#ifdef CONFIG_IP_FIREWALL
-	int fw_res = 0;		/* Forwarding result */	
-#ifdef CONFIG_IP_MASQUERADE	
-	struct sk_buff *skb_in = skb;	/* So we can remember if the masquerader did some swaps */
-#endif
-	
-	/* 
-	 *	See if we are allowed to forward this.
- 	 *	Note: demasqueraded fragments are always 'back'warded.
-	 */
-
-	
-	if(!(is_frag&4))
-	{
-		fw_res=ip_fw_chk(skb->h.iph, dev, ip_fw_fwd_chain, ip_fw_fwd_policy, 0);
-		switch (fw_res) {
-		case FW_ACCEPT:
-#ifdef CONFIG_IP_MASQUERADE
-		case FW_MASQUERADE:
-#endif
-			break;
-		case FW_REJECT:
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
-			/* fall thru */
-		default:
-			return -1;
-		}
-	}
-#endif
-	/*
-	 *	According to the RFC, we must first decrease the TTL field. If
-	 *	that reaches zero, we must reply an ICMP control message telling
-	 *	that the packet's lifetime expired.
-	 *
-	 *	Exception:
-	 *	We may not generate an ICMP for an ICMP. icmp_send does the
-	 *	enforcement of this so we can forget it here. It is however
-	 *	sometimes VERY important.
-	 */
-
-	iph = skb->h.iph;
-	iph->ttl--;
-
-	/*
-	 *	Re-compute the IP header checksum.
-	 *	This is inefficient. We know what has happened to the header
-	 *	and could thus adjust the checksum as Phil Karn does in KA9Q
-	 */
-
-	iph->check = ntohs(iph->check) + 0x0100;
-	if ((iph->check & 0xFF00) == 0)
-		iph->check++;		/* carry overflow */
-	iph->check = htons(iph->check);
-
-	if (iph->ttl <= 0)
-	{
-		/* Tell the sender its packet died... */
-		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
-		return -1;
-	}
-
-#ifdef CONFIG_IP_MROUTE
-	if(!(is_frag&8))
-	{
-#endif	
-		/*
-		 * OK, the packet is still valid.  Fetch its destination address,
-		 * and give it to the IP sender for further processing.
-		 */
-
-		rt = ip_rt_route(target_addr, NULL, NULL);
-		if (rt == NULL)
-		{
-			/*
-			 *	Tell the sender its packet cannot be delivered. Again
-			 *	ICMP is screened later.
-			 */
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);
-			return -1;
-		}
-	
-	
-		/*
-		 * Gosh.  Not only is the packet valid; we even know how to
-		 * forward it onto its final destination.  Can we say this
-		 * is being plain lucky?
-		 * If the router told us that there is no GW, use the dest.
-		 * IP address itself- we seem to be connected directly...
-		 */
-
-		raddr = rt->rt_gateway;
-	
-		if (raddr != 0)
-		{
-			/*
-			 *	Strict routing permits no gatewaying
-			 */
-	
-		        if (opt->is_strictroute)
-			{
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
-				return -1;
-			}
-		
-			/*
-			 *	There is a gateway so find the correct route for it.
-			 *	Gateways cannot in turn be gatewayed.
-			 */
-		}
-		else
-			raddr = target_addr;
-
-		/*
-		 *	Having picked a route we can now send the frame out.
-		 */
-
-		dev2 = rt->rt_dev;
-		/*
-		 *	In IP you never have to forward a frame on the interface that it 
-		 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
-		 *	we calculated.
-		 */
-#ifndef CONFIG_IP_NO_ICMP_REDIRECT
-		if (dev == dev2 && !((iph->saddr^iph->daddr)&dev->pa_mask) &&
-		    (rt->rt_flags&RTF_MODIFIED) && !opt->srr)
-			icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev);
-#endif
-#ifdef CONFIG_IP_MROUTE
-	}
-	else
-	{
-		/*
-		 *	Multicast route forward. Routing is already done
-		 */
-		dev2=skb->dev;
-		raddr=skb->raddr;
-		if(is_frag&16)		/* VIFF_TUNNEL mode */
-			encap=20;
-	}
-#endif	
-	
-
-	/*
-	 * We now may allocate a new buffer, and copy the datagram into it.
-	 * If the indicated interface is up and running, kick it.
-	 */
-
-	if (dev2->flags & IFF_UP)
-	{
-#ifdef CONFIG_IP_MASQUERADE
-		/*
-		 * If this fragment needs masquerading, make it so...
-		 * (Dont masquerade de-masqueraded fragments)
-		 */
-		if (!(is_frag&4) && fw_res==FW_MASQUERADE)
-			ip_fw_masquerade(&skb, dev2);
-#endif
-		IS_SKB(skb);
-
-		if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
-		  ip_statistics.IpFragFails++;
-		  icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev2->mtu, dev);
-		  return -1;
-		}
-
-#ifdef CONFIG_IP_MROUTE
-		if(skb_headroom(skb)-encap<dev2->hard_header_len)
-		{
-			skb2 = alloc_skb(dev2->hard_header_len + skb->len + encap + 15, GFP_ATOMIC);
-#else
-		if(skb_headroom(skb)<dev2->hard_header_len)
-		{
-			skb2 = alloc_skb(dev2->hard_header_len + skb->len + 15, GFP_ATOMIC);
-#endif		
-			/*
-			 *	This is rare and since IP is tolerant of network failures
-			 *	quite harmless.
-			 */
-		
-			if (skb2 == NULL)
-			{
-				NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
-				return -1;
-			}
-		
-			IS_SKB(skb2);
-			/*
-			 *	Add the physical headers.
-			 */
-#ifdef CONFIG_IP_MROUTE
-			if(is_frag&16)
-			{
-				skb_reserve(skb,(encap+dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
-				ip_encap(skb2,skb->len, dev2, raddr);
-			}
-			else
-#endif			
-		 		ip_send(skb2,raddr,skb->len,dev2,dev2->pa_addr);
-
-			/*
-			 *	We have to copy the bytes over as the new header wouldn't fit
-			 *	the old buffer. This should be very rare.
-			 */		 
-		 	
-			ptr = skb_put(skb2,skb->len);
-			skb2->free = 1;
-			skb2->h.raw = ptr;
-
-			/*
-			 *	Copy the packet data into the new buffer.
-			 */
-			memcpy(ptr, skb->h.raw, skb->len);
-			memcpy(skb2->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
-			iph = skb2->ip_hdr = skb2->h.iph;
-		}
-		else
-		{
-			/* 
-			 *	Build a new MAC header. 
-			 */
-
-			skb2 = skb;		
-			skb2->dev=dev2;
-#ifdef CONFIG_IP_MROUTE
-			if(is_frag&16)
-				ip_encap(skb,skb->len, dev2, raddr);
-			else
-			{
-#endif
-				skb->arp=1;
-				skb->raddr=raddr;
-				if(dev2->hard_header)
-				{
-					if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0)
-						skb->arp=0;
-				}
-#ifdef CONFIG_IP_MROUTE
-			}				
-#endif			
-			ip_statistics.IpForwDatagrams++;
-		}
-
-		if (opt->optlen) {
-			unsigned char * optptr;
-			if (opt->rr_needaddr) {
-				optptr = (unsigned char *)iph + opt->rr;
-				memcpy(&optptr[optptr[2]-5], &dev2->pa_addr, 4);
-				opt->is_changed = 1;
-			}
-			if (opt->srr_is_hit) {
-				int srrptr, srrspace;
-
-				optptr = (unsigned char *)iph + opt->srr;
-
-				for ( srrptr=optptr[2], srrspace = optptr[1];
-				      srrptr <= srrspace;
-				     srrptr += 4
-				    ) {
-					if (srrptr + 3 > srrspace)
-					  break;
-					if (memcmp(&target_addr, &optptr[srrptr-1], 4) == 0)
-					  break;
-				}
-				if (srrptr + 3 <= srrspace) {
-					opt->is_changed = 1;
-					memcpy(&optptr[srrptr-1], &dev2->pa_addr, 4);
-					iph->daddr = target_addr;
-					optptr[2] = srrptr+4;
-				} else
-				        printk("ip_forward(): Argh! Destination lost!\n");
-			}
-			if (opt->ts_needaddr) {
-				optptr = (unsigned char *)iph + opt->ts;
-				memcpy(&optptr[optptr[2]-9], &dev2->pa_addr, 4);
-				opt->is_changed = 1;
-			}
-			if (opt->is_changed) {
-				opt->is_changed = 0;
-				ip_send_check(iph);
-			}
-		}
-/*
- * ANK:  this is point of "no return", we cannot send an ICMP,
- *       because we changed SRR option.
- */
-
-		/*
-		 *	See if it needs fragmenting. Note in ip_rcv we tagged
-		 *	the fragment type. This must be right so that
-		 *	the fragmenter does the right thing.
-		 */
-
-		if(skb2->len > dev2->mtu + dev2->hard_header_len)
-		{
-			ip_fragment(NULL,skb2,dev2, is_frag);
-			kfree_skb(skb2,FREE_WRITE);
-		}
-		else
-		{
-#ifdef CONFIG_IP_ACCT		
-			/*
-			 *	Count mapping we shortcut
-			 */
-			 
-			ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
-#endif			
-			
-			/*
-			 *	Map service types to priority. We lie about
-			 *	throughput being low priority, but it's a good
-			 *	choice to help improve general usage.
-			 */
-			if(iph->tos & IPTOS_LOWDELAY)
-				dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
-			else if(iph->tos & IPTOS_THROUGHPUT)
-				dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
-			else
-				dev_queue_xmit(skb2, dev2, SOPRI_NORMAL);
-		}
-	}
-	else
-		return -1;
-	
-	/*
-	 *	Tell the caller if their buffer is free.
-	 */	 
-	 
-	if(skb==skb2)
-		return 0;	
-
-#ifdef CONFIG_IP_MASQUERADE	
-	/*
-	 *	The original is free. Free our copy and
-	 *	tell the caller not to free.
-	 */
-	if(skb!=skb_in)
-	{
-		kfree_skb(skb_in, FREE_WRITE);
-		return 0;
-	}
-#endif	
-	return 1;
-}
-
-
-#endif
-
-
-/*
- *	This function receives all incoming IP datagrams.
- *
- *	On entry skb->data points to the start of the IP header and
- *	the MAC header has been removed.
- */
-
-int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-	struct iphdr *iph = skb->h.iph;
-	struct sock *raw_sk=NULL;
-	unsigned char hash;
-	unsigned char flag = 0;
-	struct inet_protocol *ipprot;
-	int brd=IS_MYADDR;
-	struct options * opt = NULL;
-	int is_frag=0;
-#ifdef CONFIG_IP_FIREWALL
-	int err;
-#endif	
-#ifdef CONFIG_IP_MROUTE
-	int mroute_pkt=0;
-#endif	
-
-#ifdef CONFIG_NET_IPV6
-	/* 
-	 *	Intercept IPv6 frames. We dump ST-II and invalid types just below..
-	 */
-	 
-	if(iph->version == 6)
-		return ipv6_rcv(skb,dev,pt);
-#endif		
-
-	ip_statistics.IpInReceives++;
-
-	/*
-	 *	Tag the ip header of this packet so we can find it
-	 */
-
-	skb->ip_hdr = iph;
-
-	/*
-	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
-	 *	RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING].
-	 *
-	 *	Is the datagram acceptable?
-	 *
-	 *	1.	Length at least the size of an ip header
-	 *	2.	Version of 4
-	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
-	 *	4.	Doesn't have a bogus length
-	 *	(5.	We ought to check for IP multicast addresses and undefined types.. does this matter ?)
-	 */
-
-	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
-		|| skb->len < ntohs(iph->tot_len))
-	{
-		ip_statistics.IpInHdrErrors++;
-		kfree_skb(skb, FREE_WRITE);
-		return(0);
-	}
-
-	/*
-	 *	Our transport medium may have padded the buffer out. Now we know it
-	 *	is IP we can trim to the true length of the frame.
-	 *	Note this now means skb->len holds ntohs(iph->tot_len).
-	 */
-
-	skb_trim(skb,ntohs(iph->tot_len));
-
-	if (iph->ihl > 5) {
-		skb->ip_summed = 0;
-		if (ip_options_compile(NULL, skb))
-			return(0);
-		opt = (struct options*)skb->proto_priv;
-#ifdef CONFIG_IP_NOSR
-		if (opt->srr) {
-			kfree_skb(skb, FREE_READ);
-			return -EINVAL;
-		}
-#endif					
-	}
-	
-	/*
-	 *	See if the firewall wants to dispose of the packet. 
-	 */
-
-#ifdef	CONFIG_IP_FIREWALL
-	
-	if ((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy, 0))<FW_ACCEPT)
-	{
-		if(err==FW_REJECT)
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
-		kfree_skb(skb, FREE_WRITE);
-		return 0;	
-	}
-
-#endif
-	
-	/*
-	 *	Remember if the frame is fragmented.
-	 */
-	 
-	if(iph->frag_off)
-	{
-		if (iph->frag_off & htons(IP_MF))
-			is_frag|=1;
-		/*
-		 *	Last fragment ?
-		 */
-	
-		if (iph->frag_off & htons(IP_OFFSET))
-			is_frag|=2;
-	}
-	
-	/*
-	 *	Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday.
-	 *
-	 *	This is inefficient. While finding out if it is for us we could also compute
-	 *	the routing table entry. This is where the great unified cache theory comes
-	 *	in as and when someone implements it
-	 *
-	 *	For most hosts over 99% of packets match the first conditional
-	 *	and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at
-	 *	function entry.
-	 */
-
-	if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
-	{
-	        if (opt && opt->srr) {
-			int srrspace, srrptr;
-			__u32 nexthop;
-			unsigned char * optptr = ((unsigned char *)iph) + opt->srr;
-
-			if (brd != IS_MYADDR || skb->pkt_type != PACKET_HOST) {
-				kfree_skb(skb, FREE_WRITE);
-				return 0;
-			}
-
-			for ( srrptr=optptr[2], srrspace = optptr[1];
-			      srrptr <= srrspace;
-			      srrptr += 4
-			     ) 
-			{
-				int brd2;
-				if (srrptr + 3 > srrspace) 
-				{
-					icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2,
-						  skb->dev);
-					kfree_skb(skb, FREE_WRITE);
-					return 0;
-				}
-				memcpy(&nexthop, &optptr[srrptr-1], 4);
-				if ((brd2 = ip_chk_addr(nexthop)) == 0)
-					break;
-				if (brd2 != IS_MYADDR) 
-				{
-
-					/*
-					 *	ANK: should we implement weak tunneling of multicasts?
-					 *	Are they obsolete? DVMRP specs (RFC-1075) is old enough...
-					 *	[They are obsolete]
-					 */
-					kfree_skb(skb, FREE_WRITE);
-					return -EINVAL;
-				}
-			}
-			if (srrptr <= srrspace) 
-			{
-				opt->srr_is_hit = 1;
-				opt->is_changed = 1;
-#ifdef CONFIG_IP_FORWARD
-				if (ip_forward(skb, dev, is_frag, nexthop))
-					kfree_skb(skb, FREE_WRITE);
-#else
-				ip_statistics.IpInAddrErrors++;
-				kfree_skb(skb, FREE_WRITE);
-#endif
-				return 0;
-			}
-		}
-
-#ifdef CONFIG_IP_MULTICAST	
-		if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))
-		{
-			/*
-			 *	Check it is for one of our groups
-			 */
-			struct ip_mc_list *ip_mc=dev->ip_mc_list;
-			do
-			{
-				if(ip_mc==NULL)
-				{	
-					kfree_skb(skb, FREE_WRITE);
-					return 0;
-				}
-				if(ip_mc->multiaddr==iph->daddr)
-					break;
-				ip_mc=ip_mc->next;
-			}
-			while(1);
-		}
-#endif
-
-#ifdef CONFIG_IP_MASQUERADE
-		/*
-		 * Do we need to de-masquerade this fragment?
-		 */
-		if (ip_fw_demasquerade(skb)) 
-		{
-			struct iphdr *iph=skb->h.iph;
-			if (ip_forward(skb, dev, is_frag|4, iph->daddr))
-				kfree_skb(skb, FREE_WRITE);
-			return(0);
-		}
-#endif
-
-		/*
-		 *	Account for the packet
-		 */
- 
-#ifdef CONFIG_IP_ACCT
-		ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
-#endif	
-
-		/*
-		 *	Reassemble IP fragments.
-		 */
-
-		if(is_frag)
-		{
-			/* Defragment. Obtain the complete packet if there is one */
-			skb=ip_defrag(iph,skb,dev);
-			if(skb==NULL)
-				return 0;
-			skb->dev = dev;
-			iph=skb->h.iph;
-		}
-
-		/*
-		 *	Point into the IP datagram, just past the header.
-		 */
-
-		skb->ip_hdr = iph;
-		skb->h.raw += iph->ihl*4;
-
-#ifdef CONFIG_IP_MROUTE		
-		/*
-		 *	Check the state on multicast routing (multicast and not 224.0.0.z)
-		 */
-		 
-		if(brd==IS_MULTICAST && (iph->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000))
-			mroute_pkt=1;
-
-#endif
-		/*
-		 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
-		 *
-		 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
-		 */
- 
-		hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
-
-		/* 
-		 *	If there maybe a raw socket we must check - if not we don't care less 
-		 */
-		 
-		if((raw_sk=raw_prot.sock_array[hash])!=NULL)
-		{
-			struct sock *sknext=NULL;
-			struct sk_buff *skb1;
-			raw_sk=get_sock_raw(raw_sk, iph->protocol,  iph->saddr, iph->daddr);
-			if(raw_sk)	/* Any raw sockets */
-			{
-				do
-				{
-					/* Find the next */
-					sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
-					if(sknext)
-						skb1=skb_clone(skb, GFP_ATOMIC);
-					else
-						break;	/* One pending raw socket left */
-					if(skb1)
-						raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);
-					raw_sk=sknext;
-				}
-				while(raw_sk!=NULL);
-				
-				/*
-				 *	Here either raw_sk is the last raw socket, or NULL if none 
-				 */
-				 
-				/*
-				 *	We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy 
-				 */
-			}
-		}
-	
-		/*
-		 *	skb->h.raw now points at the protocol beyond the IP header.
-		 */
-	
-		hash = iph->protocol & (MAX_INET_PROTOS -1);
-		for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
-		{
-			struct sk_buff *skb2;
-	
-			if (ipprot->protocol != iph->protocol)
-				continue;
-		       /*
-			* 	See if we need to make a copy of it.  This will
-			* 	only be set if more than one protocol wants it.
-			* 	and then not for the last one. If there is a pending
-			*	raw delivery wait for that
-			*/
-	
-#ifdef CONFIG_IP_MROUTE
-			if (ipprot->copy || raw_sk || mroute_pkt)
-#else	
-			if (ipprot->copy || raw_sk)
-#endif			
-			{
-				skb2 = skb_clone(skb, GFP_ATOMIC);
-				if(skb2==NULL)
-					continue;
-			}
-			else
-			{
-				skb2 = skb;
-			}
-			flag = 1;
-
-		       /*
-			*	Pass on the datagram to each protocol that wants it,
-			*	based on the datagram protocol.  We should really
-			*	check the protocol handler's return values here...
-			*/
-
-			ipprot->handler(skb2, dev, opt, iph->daddr,
-				(ntohs(iph->tot_len) - (iph->ihl * 4)),
-				iph->saddr, 0, ipprot);
-		}
-
-		/*
-		 *	All protocols checked.
-		 *	If this packet was a broadcast, we may *not* reply to it, since that
-		 *	causes (proven, grin) ARP storms and a leakage of memory (i.e. all
-		 *	ICMP reply messages get queued up for transmission...)
-		 */
-
-#ifdef CONFIG_IP_MROUTE		 
-		/*
-		 *	Forward the last copy to the multicast router. If
-		 *	there is a pending raw deliery however make a copy
-		 *	and forward that.
-		 */
-		 
-		if(mroute_pkt)
-		{
-			flag=1;
-			if(raw_sk==NULL)
-				ipmr_forward(skb, is_frag);
-			else
-			{
-				struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
-				if(skb2)
-				{
-					skb2->free=1;
-					ipmr_forward(skb2, is_frag);
-				}
-			}
-		}
-#endif		
-
-		if(raw_sk!=NULL)	/* Shift to last raw user */
-			raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);
-		else if (!flag)		/* Free and report errors */
-		{
-			if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);	
-			kfree_skb(skb, FREE_WRITE);
-		}
-
-		return(0);
-	}
-
-	/*
-	 *	Do any unicast IP forwarding required.
-	 */
-	
-	/*
-	 *	Don't forward multicast or broadcast frames.
-	 */
-
-	if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)
-	{
-		kfree_skb(skb,FREE_WRITE);
-		return 0;
-	}
-
-	/*
-	 *	The packet is for another target. Forward the frame
-	 */
-
-#ifdef CONFIG_IP_FORWARD
-	if (opt && opt->is_strictroute) {
-	      icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev);
-	      kfree_skb(skb, FREE_WRITE);
-	      return -1;
-	}
-	if (ip_forward(skb, dev, is_frag, iph->daddr))
-		kfree_skb(skb, FREE_WRITE);
-#else
-/*	printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
-			iph->saddr,iph->daddr);*/
-	ip_statistics.IpInAddrErrors++;
-	kfree_skb(skb, FREE_WRITE);
-#endif
-	return(0);
-}
-	
-
-/*
- *	Loop a packet back to the sender.
- */
- 
-static void ip_loopback(struct device *old_dev, struct sk_buff *skb)
-{
-	struct device *dev=&loopback_dev;
-	int len=ntohs(skb->ip_hdr->tot_len);
-	struct sk_buff *newskb=dev_alloc_skb(len+dev->hard_header_len+15);
-	
-	if(newskb==NULL)
-		return;
-		
-	newskb->link3=NULL;
-	newskb->sk=NULL;
-	newskb->dev=dev;
-	newskb->saddr=skb->saddr;
-	newskb->daddr=skb->daddr;
-	newskb->raddr=skb->raddr;
-	newskb->free=1;
-	newskb->lock=0;
-	newskb->users=0;
-	newskb->pkt_type=skb->pkt_type;
-	
-	/*
-	 *	Put a MAC header on the packet
-	 */
-	ip_send(newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
-	/*
-	 *	Add the rest of the data space.	
-	 */
-	newskb->ip_hdr=(struct iphdr *)skb_put(newskb, len);
-	memcpy(newskb->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
-
-	/*
-	 *	Copy the data
-	 */
-	memcpy(newskb->ip_hdr,skb->ip_hdr,len);
-
-	/* Recurse. The device check against IFF_LOOPBACK will stop infinite recursion */
-		
-	/*printk("Loopback output queued [%lX to %lX].\n", newskb->ip_hdr->saddr,newskb->ip_hdr->daddr);*/
-	ip_queue_xmit(NULL, dev, newskb, 1);
-}
-
-
-/*
- * Queues a packet to be sent, and starts the transmitter
- * if necessary.  if free = 1 then we free the block after
- * transmit, otherwise we don't. If free==2 we not only
- * free the block but also don't assign a new ip seq number.
- * This routine also needs to put in the total length,
- * and compute the checksum
- */
-
-void ip_queue_xmit(struct sock *sk, struct device *dev,
-	      struct sk_buff *skb, int free)
-{
-	struct iphdr *iph;
-/*	unsigned char *ptr;*/
-
-	/* Sanity check */
-	if (dev == NULL)
-	{
-		NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n"));
-		return;
-	}
-
-	IS_SKB(skb);
-
-	/*
-	 *	Do some book-keeping in the packet for later
-	 */
-
-
-	skb->dev = dev;
-	skb->when = jiffies;
-
-	/*
-	 *	Find the IP header and set the length. This is bad
-	 *	but once we get the skb data handling code in the
-	 *	hardware will push its header sensibly and we will
-	 *	set skb->ip_hdr to avoid this mess and the fixed
-	 *	header length problem
-	 */
-
-	iph = skb->ip_hdr;
-	iph->tot_len = ntohs(skb->len-(((unsigned char *)iph)-skb->data));
-
-#ifdef CONFIG_IP_FIREWALL
-	if(ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy, 0) < FW_ACCEPT)
-		/* just don't send this packet */
-		return;
-#endif	
-
-	/*
-	 *	No reassigning numbers to fragments...
-	 */
-
-	if(free!=2)
-		iph->id      = htons(ip_id_count++);
-	else
-		free=1;
-
-	/* All buffers without an owner socket get freed */
-	if (sk == NULL)
-		free = 1;
-
-	skb->free = free;
-
-	/*
-	 *	Do we need to fragment. Again this is inefficient.
-	 *	We need to somehow lock the original buffer and use
-	 *	bits of it.
-	 */
-
-	if(ntohs(iph->tot_len)> dev->mtu)
-	{
-		ip_fragment(sk,skb,dev,0);
-		IS_SKB(skb);
-		kfree_skb(skb,FREE_WRITE);
-		return;
-	}
-
-	/*
-	 *	Add an IP checksum
-	 */
-
-	ip_send_check(iph);
-
-	/*
-	 *	Print the frame when debugging
-	 */
-
-	/*
-	 *	More debugging. You cannot queue a packet already on a list
-	 *	Spot this and moan loudly.
-	 */
-	if (skb->next != NULL)
-	{
-		NETDEBUG(printk("ip_queue_xmit: next != NULL\n"));
-		skb_unlink(skb);
-	}
-
-	/*
-	 *	If a sender wishes the packet to remain unfreed
-	 *	we add it to his send queue. This arguably belongs
-	 *	in the TCP level since nobody else uses it. BUT
-	 *	remember IPng might change all the rules.
-	 */
-
-	if (!free)
-	{
-		unsigned long flags;
-		/* The socket now has more outstanding blocks */
-
-		sk->packets_out++;
-
-		/* Protect the list for a moment */
-		save_flags(flags);
-		cli();
-
-		if (skb->link3 != NULL)
-		{
-			NETDEBUG(printk("ip.c: link3 != NULL\n"));
-			skb->link3 = NULL;
-		}
-		if (sk->send_head == NULL)
-		{
-			sk->send_tail = skb;
-			sk->send_head = skb;
-		}
-		else
-		{
-			sk->send_tail->link3 = skb;
-			sk->send_tail = skb;
-		}
-		/* skb->link3 is NULL */
-
-		/* Interrupt restore */
-		restore_flags(flags);
-	}
-	else
-		/* Remember who owns the buffer */
-		skb->sk = sk;
-
-	/*
-	 *	If the indicated interface is up and running, send the packet.
-	 */
-	 
-	ip_statistics.IpOutRequests++;
-#ifdef CONFIG_IP_ACCT
-	ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
-#endif	
-	
-#ifdef CONFIG_IP_MULTICAST	
-
-	/*
-	 *	Multicasts are looped back for other local users
-	 */
-	 
-	if (MULTICAST(iph->daddr) && !(dev->flags&IFF_LOOPBACK))
-	{
-		if(sk==NULL || sk->ip_mc_loop)
-		{
-			if(iph->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
-			{
-				ip_loopback(dev,skb);
-			}
-			else
-			{
-				struct ip_mc_list *imc=dev->ip_mc_list;
-				while(imc!=NULL)
-				{
-					if(imc->multiaddr==iph->daddr)
-					{
-						ip_loopback(dev,skb);
-						break;
-					}
-					imc=imc->next;
-				}
-			}
-		}
-		/* Multicasts with ttl 0 must not go beyond the host */
-		
-		if(skb->ip_hdr->ttl==0)
-		{
-			kfree_skb(skb, FREE_READ);
-			return;
-		}
-	}
-#endif
-	if((dev->flags&IFF_BROADCAST) && (iph->daddr==dev->pa_brdaddr||iph->daddr==0xFFFFFFFF) && !(dev->flags&IFF_LOOPBACK))
-		ip_loopback(dev,skb);
-		
-	if (dev->flags & IFF_UP)
-	{
-		/*
-		 *	If we have an owner use its priority setting,
-		 *	otherwise use NORMAL
-		 */
-
-		if (sk != NULL)
-		{
-			dev_queue_xmit(skb, dev, sk->priority);
-		}
-		else
-		{
-			dev_queue_xmit(skb, dev, SOPRI_NORMAL);
-		}
-	}
-	else
-	{
-		if(sk)
-			sk->err = ENETDOWN;
-		ip_statistics.IpOutDiscards++;
-		if (free)
-			kfree_skb(skb, FREE_WRITE);
-	}
-}
-
-
-
-#ifdef CONFIG_IP_MULTICAST
-
-/*
- *	Write an multicast group list table for the IGMP daemon to
- *	read.
- */
- 
-int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	off_t pos=0, begin=0;
-	struct ip_mc_list *im;
-	unsigned long flags;
-	int len=0;
-	struct device *dev;
-	
-	len=sprintf(buffer,"Device    : Count\tGroup    Users Timer\n");  
-	save_flags(flags);
-	cli();
-	
-	for(dev = dev_base; dev; dev = dev->next)
-	{
-                if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST))
-                {
-                        len+=sprintf(buffer+len,"%-10s: %5d\n",
-					dev->name, dev->mc_count);
-                        for(im = dev->ip_mc_list; im; im = im->next)
-                        {
-                                len+=sprintf(buffer+len,
-					"\t\t\t%08lX %5d %d:%08lX\n",
-                                        im->multiaddr, im->users,
-					im->tm_running, im->timer.expires-jiffies);
-                                pos=begin+len;
-                                if(pos<offset)
-                                {
-                                        len=0;
-                                        begin=pos;
-                                }
-                                if(pos>offset+length)
-                                        break;
-                        }
-                }
-	}
-	restore_flags(flags);
-	*start=buffer+(offset-begin);
-	len-=(offset-begin);
-	if(len>length)
-		len=length;	
-	return len;
-}
-
-
-/*
- *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
- *	an IP socket.
- *
- *	We implement IP_TOS (type of service), IP_TTL (time to live).
- *
- *	Next release we will sort out IP_OPTIONS since for some people are kind of important.
- */
-
-static struct device *ip_mc_find_devfor(unsigned long addr)
-{
-	struct device *dev;
-	for(dev = dev_base; dev; dev = dev->next)
-	{
-		if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
-			(dev->pa_addr==addr))
-			return dev;
-	}
-
-	return NULL;
-}
-
-#endif
-
-int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
-{
-	int val,err;
-	unsigned char ucval;
-#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
-	struct ip_fw tmp_fw;
-#endif	
-	if (optval == NULL)
-	{
-		val=0;
-		ucval=0;
-	}
-	else
-	{
-		err=verify_area(VERIFY_READ, optval, sizeof(int));
-		if(err)
-			return err;
-		val = get_user((int *) optval);
-		ucval=get_user((unsigned char *) optval);
-	}
-	
-	if(level!=SOL_IP)
-		return -EOPNOTSUPP;
-#ifdef CONFIG_IP_MROUTE
-	if(optname>=MRT_BASE && optname <=MRT_BASE+10)
-	{
-		return ip_mroute_setsockopt(sk,optname,optval,optlen);
-	}
-#endif
-	
-	switch(optname)
-	{
-		case IP_OPTIONS:
-	          {
-			  struct options * opt = NULL;
-			  struct options * old_opt;
-			  if (optlen > 40 || optlen < 0)
-			    return -EINVAL;
-			  err = verify_area(VERIFY_READ, optval, optlen);
-			  if (err)
-			    return err;
-			  opt = kmalloc(sizeof(struct options)+((optlen+3)&~3), GFP_KERNEL);
-			  if (!opt)
-			    return -ENOMEM;
-			  memset(opt, 0, sizeof(struct options));
-			  if (optlen)
-			    memcpy_fromfs(opt->__data, optval, optlen);
-			  while (optlen & 3)
-			    opt->__data[optlen++] = IPOPT_END;
-			  opt->optlen = optlen;
-			  opt->is_data = 1;
-			  opt->is_setbyuser = 1;
-			  if (optlen && ip_options_compile(opt, NULL)) {
-				  kfree_s(opt, sizeof(struct options) + optlen);
-				  return -EINVAL;
-			  }
-			  /*
-			   * ANK: I'm afraid that receive handler may change
-			   * options from under us.
-			   */
-			  cli();
-			  old_opt = sk->opt;
-			  sk->opt = opt;
-			  sti();
-			  if (old_opt)
-			    kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
-			  return 0;
-		  }
-		case IP_TOS:
-			if(val<0||val>255)
-				return -EINVAL;
-			sk->ip_tos=val;
-			if(val==IPTOS_LOWDELAY)
-				sk->priority=SOPRI_INTERACTIVE;
-			if(val==IPTOS_THROUGHPUT)
-				sk->priority=SOPRI_BACKGROUND;
-			return 0;
-		case IP_TTL:
-			if(val<1||val>255)
-				return -EINVAL;
-			sk->ip_ttl=val;
-			return 0;
-		case IP_HDRINCL:
-			if(sk->type!=SOCK_RAW)
-				return -ENOPROTOOPT;
-			sk->ip_hdrincl=val?1:0;
-			return 0;
-#ifdef CONFIG_IP_MULTICAST
-		case IP_MULTICAST_TTL: 
-		{
-			sk->ip_mc_ttl=(int)ucval;
-	                return 0;
-		}
-		case IP_MULTICAST_LOOP: 
-		{
-			if(ucval!=0 && ucval!=1)
-				 return -EINVAL;
-			sk->ip_mc_loop=(int)ucval;
-			return 0;
-		}
-		case IP_MULTICAST_IF: 
-		{
-			struct in_addr addr;
-			struct device *dev=NULL;
-			
-			/*
-			 *	Check the arguments are allowable
-			 */
-
-			err=verify_area(VERIFY_READ, optval, sizeof(addr));
-			if(err)
-				return err;
-				
-			memcpy_fromfs(&addr,optval,sizeof(addr));
-			
-			
-			/*
-			 *	What address has been requested
-			 */
-			
-			if(addr.s_addr==INADDR_ANY)	/* Default */
-			{
-				sk->ip_mc_name[0]=0;
-				return 0;
-			}
-			
-			/*
-			 *	Find the device
-			 */
-			 
-			dev=ip_mc_find_devfor(addr.s_addr);
-						
-			/*
-			 *	Did we find one
-			 */
-			 
-			if(dev) 
-			{
-				strcpy(sk->ip_mc_name,dev->name);
-				return 0;
-			}
-			return -EADDRNOTAVAIL;
-		}
-		
-		case IP_ADD_MEMBERSHIP: 
-		{
-		
-/*
- *	FIXME: Add/Del membership should have a semaphore protecting them from re-entry
- */
-			struct ip_mreq mreq;
-			__u32 route_src;
-			struct rtable *rt;
-			struct device *dev=NULL;
-			
-			/*
-			 *	Check the arguments.
-			 */
-
-			err=verify_area(VERIFY_READ, optval, sizeof(mreq));
-			if(err)
-				return err;
-
-			memcpy_fromfs(&mreq,optval,sizeof(mreq));
-
-			/* 
-			 *	Get device for use later
-			 */
-
-			if(mreq.imr_interface.s_addr==INADDR_ANY) 
-			{
-				/*
-				 *	Not set so scan.
-				 */
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
-				{
-					dev=rt->rt_dev;
-					rt->rt_use--;
-				}
-			}
-			else
-			{
-				/*
-				 *	Find a suitable device.
-				 */
-				
-				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
-			}
-			
-			/*
-			 *	No device, no cookies.
-			 */
-			 
-			if(!dev)
-				return -ENODEV;
-				
-			/*
-			 *	Join group.
-			 */
-			 
-			return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr);
-		}
-		
-		case IP_DROP_MEMBERSHIP: 
-		{
-			struct ip_mreq mreq;
-			struct rtable *rt;
-			__u32 route_src;
-			struct device *dev=NULL;
-
-			/*
-			 *	Check the arguments
-			 */
-			 
-			err=verify_area(VERIFY_READ, optval, sizeof(mreq));
-			if(err)
-				return err;
-
-			memcpy_fromfs(&mreq,optval,sizeof(mreq));
-
-			/*
-			 *	Get device for use later 
-			 */
- 
-			if(mreq.imr_interface.s_addr==INADDR_ANY) 
-			{
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
-			        {
-					dev=rt->rt_dev;
-					rt->rt_use--;
-				}
-			}
-			else 
-			{
-			
-				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
-			}
-			
-			/*
-			 *	Did we find a suitable device.
-			 */
-			 
-			if(!dev)
-				return -ENODEV;
-				
-			/*
-			 *	Leave group
-			 */
-			 
-			return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
-		}
-#endif			
-#ifdef CONFIG_IP_FIREWALL
-		case IP_FW_ADD_BLK:
-		case IP_FW_DEL_BLK:
-		case IP_FW_ADD_FWD:
-		case IP_FW_DEL_FWD:
-		case IP_FW_CHK_BLK:
-		case IP_FW_CHK_FWD:
-		case IP_FW_FLUSH_BLK:
-		case IP_FW_FLUSH_FWD:
-		case IP_FW_ZERO_BLK:
-		case IP_FW_ZERO_FWD:
-		case IP_FW_POLICY_BLK:
-		case IP_FW_POLICY_FWD:
-			if(!suser())
-				return -EPERM;
-			if(optlen>sizeof(tmp_fw) || optlen<1)
-				return -EINVAL;
-			err=verify_area(VERIFY_READ,optval,optlen);
-			if(err)
-				return err;
-			memcpy_fromfs(&tmp_fw,optval,optlen);
-			err=ip_fw_ctl(optname, &tmp_fw,optlen);
-			return -err;	/* -0 is 0 after all */
-			
-#endif
-#ifdef CONFIG_IP_ACCT
-		case IP_ACCT_DEL:
-		case IP_ACCT_ADD:
-		case IP_ACCT_FLUSH:
-		case IP_ACCT_ZERO:
-			if(!suser())
-				return -EPERM;
-			if(optlen>sizeof(tmp_fw) || optlen<1)
-				return -EINVAL;
-			err=verify_area(VERIFY_READ,optval,optlen);
-			if(err)
-				return err;
-			memcpy_fromfs(&tmp_fw, optval,optlen);
-			err=ip_acct_ctl(optname, &tmp_fw,optlen);
-			return -err;	/* -0 is 0 after all */
-#endif
-		/* IP_OPTIONS and friends go here eventually */
-		default:
-			return(-ENOPROTOOPT);
-	}
-}
-
-/*
- *	Get the options. Note for future reference. The GET of IP options gets the
- *	_received_ ones. The set sets the _sent_ ones.
- */
-
-int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
-{
-	int val,err;
-#ifdef CONFIG_IP_MULTICAST
-	int len;
-#endif
-	
-	if(level!=SOL_IP)
-		return -EOPNOTSUPP;
-
-#ifdef CONFIG_IP_MROUTE
-	if(optname>=MRT_BASE && optname <=MRT_BASE+10)
-	{
-		return ip_mroute_getsockopt(sk,optname,optval,optlen);
-	}
-#endif
-
-	switch(optname)
-	{
-		case IP_OPTIONS:
-			{
-				unsigned char optbuf[sizeof(struct options)+40];
-				struct options * opt = (struct options*)optbuf;
-				err = verify_area(VERIFY_WRITE, optlen, sizeof(int));
-				if (err)
-				  return err;
-				cli();
-				opt->optlen = 0;
-				if (sk->opt)
-				  memcpy(optbuf, sk->opt, sizeof(struct options)+sk->opt->optlen);
-				sti();
-				if (opt->optlen == 0) {
-					put_fs_long(0,(unsigned long *) optlen);
-					return 0;
-				}
-				err = verify_area(VERIFY_WRITE, optval, opt->optlen);
-				if (err)
-				  return err;
-/*
- * Now we should undo all the changes done by ip_options_compile().
- */
-				if (opt->srr) {
-					unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
-					memmove(optptr+7, optptr+4, optptr[1]-7);
-					memcpy(optptr+3, &opt->faddr, 4);
-				}
-				if (opt->rr_needaddr) {
-					unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
-					memset(&optptr[optptr[2]-1], 0, 4);
-					optptr[2] -= 4;
-				}
-				if (opt->ts) {
-					unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
-					if (opt->ts_needtime) {
-						memset(&optptr[optptr[2]-1], 0, 4);
-						optptr[2] -= 4;
-					}
-					if (opt->ts_needaddr) {
-						memset(&optptr[optptr[2]-1], 0, 4);
-						optptr[2] -= 4;
-					}
-				}
-				put_fs_long(opt->optlen, (unsigned long *) optlen);
-				memcpy_tofs(optval, opt->__data, opt->optlen);
-			}
-			return 0;
-		case IP_TOS:
-			val=sk->ip_tos;
-			break;
-		case IP_TTL:
-			val=sk->ip_ttl;
-			break;
-		case IP_HDRINCL:
-			val=sk->ip_hdrincl;
-			break;
-#ifdef CONFIG_IP_MULTICAST			
-		case IP_MULTICAST_TTL:
-			val=sk->ip_mc_ttl;
-			break;
-		case IP_MULTICAST_LOOP:
-			val=sk->ip_mc_loop;
-			break;
-		case IP_MULTICAST_IF:
-			err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
-			if(err)
-  				return err;
-  			len=strlen(sk->ip_mc_name);
-  			err=verify_area(VERIFY_WRITE, optval, len);
-		  	if(err)
-  				return err;
-  			put_user(len,(int *) optlen);
-			memcpy_tofs((void *)optval,sk->ip_mc_name, len);
-			return 0;
-#endif
-		default:
-			return(-ENOPROTOOPT);
-	}
-	err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
-	if(err)
-		return err;
-	put_user(sizeof(int),(int *) optlen);
-
-	err=verify_area(VERIFY_WRITE, optval, sizeof(int));
-	if(err)
-		return err;
-	put_user(val,(int *) optval);
-
-	return(0);
-}
-
-/*
- *	Build and send a packet, with as little as one copy
- *
- *	Doesn't care much about ip options... option length can be
- *	different for fragment at 0 and other fragments.
- *
- *	Note that the fragment at the highest offset is sent first,
- *	so the getfrag routine can fill in the TCP/UDP checksum header
- *	field in the last fragment it sends... actually it also helps
- * 	the reassemblers, they can put most packets in at the head of
- *	the fragment queue, and they know the total size in advance. This
- *	last feature will measurable improve the Linux fragment handler.
- *
- *	The callback has five args, an arbitrary pointer (copy of frag),
- *	the source IP address (may depend on the routing table), the 
- *	destination adddress (char *), the offset to copy from, and the
- *	length to be copied.
- * 
- */
-
-int ip_build_xmit(struct sock *sk,
-		   void getfrag (const void *,
-				 __u32,
-				 char *,
-				 unsigned int,	
-				 unsigned int),
-		   const void *frag,
-		   unsigned short int length,
-		   __u32 daddr,
-		   __u32 user_saddr,
-		   struct options * opt,
-		   int flags,
-		   int type) 
-{
-	struct rtable *rt;
-	unsigned int fraglen, maxfraglen, fragheaderlen;
-	int offset, mf;
-	__u32 saddr;
-	unsigned short id;
-	struct iphdr *iph;
-	int local=0;
-	struct device *dev;
-	int nfrags=0;
-	__u32 true_daddr = daddr;
-
-	if (opt && opt->srr && !sk->ip_hdrincl)
-	  daddr = opt->faddr;
-	
-	ip_statistics.IpOutRequests++;
-
-#ifdef CONFIG_IP_MULTICAST	
-	if(sk && MULTICAST(daddr) && *sk->ip_mc_name)
-	{
-		dev=dev_get(sk->ip_mc_name);
-		if(!dev)
-			return -ENODEV;
-		rt=NULL;
-		if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
-			saddr = sk->saddr;
-		else
-			saddr = dev->pa_addr;
-	}
-	else
-	{
-#endif	
-		/*
-		 *	Perform the IP routing decisions
-		 */
-	 
-		if(sk->localroute || flags&MSG_DONTROUTE)
-			local=1;
-	
-		rt = sk->ip_route_cache;
-		
-		/*
-		 *	See if the routing cache is outdated. We need to clean this up once we are happy it is reliable
-		 *	by doing the invalidation actively in the route change and header change.
-		 */
-	
-		saddr=sk->ip_route_saddr;	 
-		if(!rt || sk->ip_route_stamp != rt_stamp ||
-		   daddr!=sk->ip_route_daddr || sk->ip_route_local!=local ||
-		   (sk->saddr && sk->saddr != saddr))
-		{
-			if(local)
-				rt = ip_rt_local(daddr, NULL, &saddr);
-			else
-				rt = ip_rt_route(daddr, NULL, &saddr);
-			sk->ip_route_local=local;
-			sk->ip_route_daddr=daddr;
-			sk->ip_route_saddr=saddr;
-			sk->ip_route_stamp=rt_stamp;
-			sk->ip_route_cache=rt;
-			sk->ip_hcache_ver=NULL;
-			sk->ip_hcache_state= 0;
-		}
-		else if(rt)
-		{
-			/*
-			 *	Attempt header caches only if the cached route is being reused. Header cache
-			 *	is not ultra cheap to set up. This means we only set it up on the second packet,
-			 *	so one shot communications are not slowed. We assume (seems reasonable) that 2 is
-			 *	probably going to be a stream of data.
-			 */
-			if(rt->rt_dev->header_cache && sk->ip_hcache_state!= -1)
-			{
-				if(sk->ip_hcache_ver==NULL || sk->ip_hcache_stamp!=*sk->ip_hcache_ver)
-					rt->rt_dev->header_cache(rt->rt_dev,sk,saddr,daddr);
-				else
-					/* Can't cache. Remember this */
-					sk->ip_hcache_state= -1;
-			}
-		}
-		
-		if (rt == NULL) 
-		{
-	 		ip_statistics.IpOutNoRoutes++;
-			return(-ENETUNREACH);
-		}
-	
-		if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
-			saddr = sk->saddr;
-			
-		dev=rt->rt_dev;
-#ifdef CONFIG_IP_MULTICAST
-	}
-#endif		
-	if (user_saddr)
-	  saddr = user_saddr;
-
-	/*
-	 *	Now compute the buffer space we require
-	 */ 
-	 
-	/*
-	 *	Try the simple case first. This leaves broadcast, multicast, fragmented frames, and by
-	 *	choice RAW frames within 20 bytes of maximum size(rare) to the long path
-	 */
-
-	length += 20;
-	if (!sk->ip_hdrincl && opt) {
-		length += opt->optlen;
-		if (opt->is_strictroute && rt && rt->rt_gateway) {
-			ip_statistics.IpOutNoRoutes++;
-			return -ENETUNREACH;
-		}
-	}
-	if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr)
-	{	
-		int error;
-		struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+dev->hard_header_len,0, 0,&error);
-		if(skb==NULL)
-		{
-			ip_statistics.IpOutDiscards++;
-			return error;
-		}
-		skb->dev=dev;
-		skb->free=1;
-		skb->when=jiffies;
-		skb->sk=sk;
-		skb->arp=0;
-		skb->saddr=saddr;
-		skb->raddr=(rt&&rt->rt_gateway)?rt->rt_gateway:daddr;
-		skb_reserve(skb,(dev->hard_header_len+15)&~15);
-		if(sk->ip_hcache_state>0)
-		{
-			memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data,dev->hard_header_len);
-			skb->arp=1;
-		}
-		else if(dev->hard_header)
-		{
-			if(dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,0)>0)
-				skb->arp=1;
-		}
-		else
-			skb->arp=1;
-		skb->ip_hdr=iph=(struct iphdr *)skb_put(skb,length);
-		dev_lock_list();
-		if(!sk->ip_hdrincl)
-		{
-			iph->version=4;
-			iph->ihl=5;
-			iph->tos=sk->ip_tos;
-			iph->tot_len = htons(length);
-			iph->id=htons(ip_id_count++);
-			iph->frag_off = 0;
-			iph->ttl=sk->ip_ttl;
-			iph->protocol=type;
-			iph->saddr=saddr;
-			iph->daddr=daddr;
-			if (opt) {
-				iph->ihl += opt->optlen>>2;
-				ip_options_build(skb, opt,
-						 true_daddr, dev->pa_addr, 0);
-			}
-			iph->check=0;
-			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-			getfrag(frag,saddr,((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
-		}
-		else
-			getfrag(frag,saddr,(void *)iph,0,length-20);
-		dev_unlock_list();
-#ifdef CONFIG_IP_FIREWALL
-		if(ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy,0) < FW_ACCEPT)
-		{
-			kfree_skb(skb, FREE_WRITE);
-			return -EPERM;
-		}
-#endif
-#ifdef CONFIG_IP_ACCT
-		ip_fw_chk((void *)skb->data,dev,ip_acct_chain, IP_FW_F_ACCEPT,1);
-#endif		
-		if(dev->flags&IFF_UP)
-			dev_queue_xmit(skb,dev,sk->priority);
-		else
-		{
-			ip_statistics.IpOutDiscards++;
-			kfree_skb(skb, FREE_WRITE);
-		}
-		return 0;
-	}
-	length-=20;
-	if (sk && !sk->ip_hdrincl && opt) {
-		length -= opt->optlen;
-		fragheaderlen = dev->hard_header_len + sizeof(struct iphdr) + opt->optlen;
-		maxfraglen = ((dev->mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
-	} else {
-		fragheaderlen = dev->hard_header_len;
-		if(!sk->ip_hdrincl)
-		  fragheaderlen += 20;
-		
-	/*
-	 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work
-	 *	out the size of the frames to send.
-	 */
-	 
-		maxfraglen = ((dev->mtu-20) & ~7) + fragheaderlen;
-        }
-	
-	/*
-	 *	Start at the end of the frame by handling the remainder.
-	 */
-	 
-	offset = length - (length % (maxfraglen - fragheaderlen));
-	
-	/*
-	 *	Amount of memory to allocate for final fragment.
-	 */
-	 
-	fraglen = length - offset + fragheaderlen;
-	
-	if(length-offset==0)
-	{
-		fraglen = maxfraglen;
-		offset -= maxfraglen-fragheaderlen;
-	}
-	
-	
-	/*
-	 *	The last fragment will not have MF (more fragments) set.
-	 */
-	 
-	mf = 0;
-
-	/*
-	 *	Can't fragment raw packets 
-	 */
-	 
-	if (sk->ip_hdrincl && offset > 0)
- 		return(-EMSGSIZE);
-
-	/*
-	 *	Lock the device lists.
-	 */
-
-	dev_lock_list();
-	
-	/*
-	 *	Get an identifier
-	 */
-	 
-	id = htons(ip_id_count++);
-
-	/*
-	 *	Being outputting the bytes.
-	 */
-	 
-	do 
-	{
-		struct sk_buff * skb;
-		int error;
-		char *data;
-
-		/*
-		 *	Get the memory we require with some space left for alignment.
-		 */
-
-		skb = sock_alloc_send_skb(sk, fraglen+15, 0, 0, &error);
-		if (skb == NULL)
-		{
-			ip_statistics.IpOutDiscards++;
-			if(nfrags>1)
-				ip_statistics.IpFragCreates++;			
-			dev_unlock_list();
-			return(error);
-		}
-		
-		/*
-		 *	Fill in the control structures
-		 */
-		 
-		skb->next = skb->prev = NULL;
-		skb->dev = dev;
-		skb->when = jiffies;
-		skb->free = 1; /* dubious, this one */
-		skb->sk = sk;
-		skb->arp = 0;
-		skb->saddr = saddr;
-		skb->raddr = (rt&&rt->rt_gateway) ? rt->rt_gateway : daddr;
-		skb_reserve(skb,(dev->hard_header_len+15)&~15);
-		data = skb_put(skb, fraglen-dev->hard_header_len);
-
-		/*
-		 *	Save us ARP and stuff. In the optimal case we do no route lookup (route cache ok)
-		 *	no ARP lookup (arp cache ok) and output. The cache checks are still too slow but
-		 *	this can be fixed later. For gateway routes we ought to have a rt->.. header cache
-		 *	pointer to speed header cache builds for identical targets.
-		 */
-		 
-		if(sk->ip_hcache_state>0)
-		{
-			memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data, dev->hard_header_len);
-			skb->arp=1;
-		}
-		else if (dev->hard_header)
-		{
-			if(dev->hard_header(skb, dev, ETH_P_IP, 
-						NULL, NULL, 0)>0)
-				skb->arp=1;
-		}
-		
-		/*
-		 *	Find where to start putting bytes.
-		 */
-		 
-		skb->ip_hdr = iph = (struct iphdr *)data;
-
-		/*
-		 *	Only write IP header onto non-raw packets 
-		 */
-		 
-		if(!sk->ip_hdrincl) 
-		{
-
-			iph->version = 4;
-			iph->ihl = 5; /* ugh */
-			if (opt) {
-				iph->ihl += opt->optlen>>2;
-				ip_options_build(skb, opt,
-						 true_daddr, dev->pa_addr, offset);
-			}
-			iph->tos = sk->ip_tos;
-			iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
-			iph->id = id;
-			iph->frag_off = htons(offset>>3);
-			iph->frag_off |= mf;
-#ifdef CONFIG_IP_MULTICAST
-			if (MULTICAST(daddr))
-				iph->ttl = sk->ip_mc_ttl;
-			else
-#endif
-				iph->ttl = sk->ip_ttl;
-			iph->protocol = type;
-			iph->check = 0;
-			iph->saddr = saddr;
-			iph->daddr = daddr;
-			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-			data += iph->ihl*4;
-			
-			/*
-			 *	Any further fragments will have MF set.
-			 */
-			 
-			mf = htons(IP_MF);
-		}
-		
-		/*
-		 *	User data callback
-		 */
-
-		getfrag(frag, saddr, data, offset, fraglen-fragheaderlen);
-		
-		/*
-		 *	Account for the fragment.
-		 */
-		 
-#ifdef CONFIG_IP_FIREWALL
-		if(!offset && ip_fw_chk(iph, dev, ip_fw_blk_chain, ip_fw_blk_policy,0) < FW_ACCEPT)
-		{
-			kfree_skb(skb, FREE_WRITE);
-			dev_unlock_list();
-			return -EPERM;
-		}
-#endif		
-#ifdef CONFIG_IP_ACCT
-		if(!offset)
-			ip_fw_chk(iph, dev, ip_acct_chain, IP_FW_F_ACCEPT, 1);
-#endif	
-		offset -= (maxfraglen-fragheaderlen);
-		fraglen = maxfraglen;
-
-#ifdef CONFIG_IP_MULTICAST
-
-		/*
-		 *	Multicasts are looped back for other local users
-		 */
-	 
-		if (MULTICAST(daddr) && !(dev->flags&IFF_LOOPBACK)) 
-		{
-			/*
-			 *	Loop back any frames. The check for IGMP_ALL_HOSTS is because
-			 *	you are always magically a member of this group.
-			 *
-			 *	Always loop back all host messages when running as a multicast router.
-			 */
-			 
-			if(sk==NULL || sk->ip_mc_loop)
-			{
-				if(skb->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
-					ip_loopback(rt?rt->rt_dev:dev,skb);
-				else 
-				{
-					struct ip_mc_list *imc=rt?rt->rt_dev->ip_mc_list:dev->ip_mc_list;
-					while(imc!=NULL) 
-					{
-						if(imc->multiaddr==daddr) 
-						{
-							ip_loopback(rt?rt->rt_dev:dev,skb);
-							break;
-						}
-						imc=imc->next;
-					}
-				}
-			}
-
-			/*
-			 *	Multicasts with ttl 0 must not go beyond the host. Fixme: avoid the
-			 *	extra clone.
-			 */
-
-			if(skb->ip_hdr->ttl==0)
-				kfree_skb(skb, FREE_READ);
-		}
-#endif
-
-		nfrags++;
-		
-		/*
-		 *	BSD loops broadcasts
-		 */
-		 
-		if((dev->flags&IFF_BROADCAST) && (daddr==0xFFFFFFFF || daddr==dev->pa_brdaddr) && !(dev->flags&IFF_LOOPBACK))
-			ip_loopback(dev,skb);
-
-		/*
-		 *	Now queue the bytes into the device.
-		 */
-		 
-		if (dev->flags & IFF_UP) 
-		{
-			dev_queue_xmit(skb, dev, sk->priority);
-		} 
-		else 
-		{
-			/*
-			 *	Whoops... 
-			 */
-			 
-			ip_statistics.IpOutDiscards++;
-			if(nfrags>1)
-				ip_statistics.IpFragCreates+=nfrags;
-			kfree_skb(skb, FREE_WRITE);
-			dev_unlock_list();
-			/*
-			 *	BSD behaviour.
-			 */
-			if(sk!=NULL)
-				sk->err=ENETDOWN;
-			return(0); /* lose rest of fragments */
-		}
-	} 
-	while (offset >= 0);
-	if(nfrags>1)
-		ip_statistics.IpFragCreates+=nfrags;
-	dev_unlock_list();
-	return(0);
-}
-    
-
-/*
- *	IP protocol layer initialiser
- */
-
-static struct packet_type ip_packet_type =
-{
-	0,	/* MUTTER ntohs(ETH_P_IP),*/
-	NULL,	/* All devices */
-	ip_rcv,
-	NULL,
-	NULL,
-};
-
-#ifdef CONFIG_RTNETLINK
-
-/*
- *	Netlink hooks for IP
- */
- 
-void ip_netlink_msg(unsigned long msg, __u32 daddr, __u32 gw, __u32 mask, short flags, short metric, char *name)
-{
-	struct sk_buff *skb=alloc_skb(sizeof(struct netlink_rtinfo), GFP_ATOMIC);
-	struct netlink_rtinfo *nrt;
-	struct sockaddr_in *s;
-	if(skb==NULL)
-		return;
-	nrt=(struct netlink_rtinfo *)skb_put(skb, sizeof(struct netlink_rtinfo));
-	nrt->rtmsg_type=msg;
-	s=(struct sockaddr_in *)&nrt->rtmsg_dst;
-	s->sin_family=AF_INET;
-	s->sin_addr.s_addr=daddr;
-	s=(struct sockaddr_in *)&nrt->rtmsg_gateway;
-	s->sin_family=AF_INET;
-	s->sin_addr.s_addr=gw;
-	s=(struct sockaddr_in *)&nrt->rtmsg_genmask;
-	s->sin_family=AF_INET;
-	s->sin_addr.s_addr=mask;
-	nrt->rtmsg_flags=flags;
-	nrt->rtmsg_metric=metric;
-	strcpy(nrt->rtmsg_device,name);
-	netlink_post(NETLINK_ROUTE, skb);
-}	
-
-#endif
-
-/*
- *	Device notifier
- */
- 
-static int ip_rt_event(unsigned long event, void *ptr)
-{
-	struct device *dev=ptr;
-	if(event==NETDEV_DOWN)
-	{
-		ip_netlink_msg(RTMSG_DELDEVICE, 0,0,0,0,0,dev->name);
-		ip_rt_flush(dev);
-	}
-/*
- *	Join the intial group if multicast.
- */		
-	if(event==NETDEV_UP)
-	{
-#ifdef CONFIG_IP_MULTICAST	
-		ip_mc_allhost(dev);
-#endif		
-		ip_netlink_msg(RTMSG_NEWDEVICE, 0,0,0,0,0,dev->name);
-	}
-	return NOTIFY_DONE;
-}
-
-struct notifier_block ip_rt_notifier={
-	ip_rt_event,
-	NULL,
-	0
-};
-
-/*
- *	IP registers the packet type and then calls the subprotocol initialisers
- */
-
-void ip_init(void)
-{
-	ip_packet_type.type=htons(ETH_P_IP);
-	dev_add_pack(&ip_packet_type);
-
-	/* So we flush routes when a device is downed */	
-	register_netdevice_notifier(&ip_rt_notifier);
-
-/*	ip_raw_init();
-	ip_packet_init();
-	ip_tcp_init();
-	ip_udp_init();*/
-
-#ifdef CONFIG_IP_MULTICAST
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_IGMP, 4, "igmp",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		ip_mc_procinfo
-	});
-#endif
-}
-
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v1.3.35/linux/net/ipv4/ip_forward.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/ip_forward.c	Tue Oct 17 13:42:36 1995
@@ -0,0 +1,444 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The IP forwarding functionality.
+ *		
+ * Authors:	see ip.c
+ *
+ * Fixes:
+ *		Many		:	Split from ip.c , see ip_input.c for history.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#include <net/checksum.h>
+#include <linux/route.h>
+#include <net/route.h>
+ 
+#ifdef CONFIG_IP_FORWARD
+#ifdef CONFIG_IP_MROUTE
+
+/*
+ * 	Encapsulate a packet by attaching a valid IPIP header to it.
+ *	This avoids tunnel drivers and other mess and gives us the speed so
+ *	important for multicast video.
+ */
+ 
+static void ip_encap(struct sk_buff *skb, int len, struct device *out, __u32 daddr)
+{
+	/*
+	 *	There is space for the IPIP header and MAC left.
+	 *
+	 *	Firstly push down and install the IPIP header.
+	 */
+	struct iphdr *iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+	if(len>65515)
+		len=65515;
+	iph->version	= 	4;
+	iph->tos	=	skb->ip_hdr->tos;
+	iph->ttl	=	skb->ip_hdr->ttl;
+	iph->frag_off	=	0;
+	iph->daddr	=	daddr;
+	iph->saddr	=	out->pa_addr;
+	iph->protocol	=	IPPROTO_IPIP;
+	iph->ihl	=	5;
+	iph->tot_len	=	htons(skb->len);
+	iph->id		=	htons(ip_id_count++);
+	ip_send_check(iph);
+
+	skb->dev = out;
+	skb->arp = 1;
+	skb->raddr=daddr;
+	/*
+	 *	Now add the physical header (driver will push it down).
+	 */
+	if (out->hard_header && out->hard_header(skb, out, ETH_P_IP, NULL, NULL, len)<0)
+			skb->arp=0;
+	/*
+	 *	Read to queue for transmission.
+	 */
+}
+
+#endif
+
+/*
+ *	Forward an IP datagram to its next destination.
+ */
+
+int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
+	       __u32 target_addr)
+{
+	struct device *dev2;	/* Output device */
+	struct iphdr *iph;	/* Our header */
+	struct sk_buff *skb2;	/* Output packet */
+	struct rtable *rt;	/* Route we use */
+	unsigned char *ptr;	/* Data pointer */
+	unsigned long raddr;	/* Router IP address */
+	struct   options * opt	= (struct options*)skb->proto_priv;
+	int encap = 0;		/* Encap length */
+#ifdef CONFIG_FIREWALL
+	int fw_res = 0;		/* Forwarding result */	
+#ifdef CONFIG_IP_MASQUERADE	
+	struct sk_buff *skb_in = skb;	/* So we can remember if the masquerader did some swaps */
+#endif
+	
+	/* 
+	 *	See if we are allowed to forward this.
+ 	 *	Note: demasqueraded fragments are always 'back'warded.
+	 */
+
+	
+	if(!(is_frag&4))
+	{
+		fw_res=call_fw_firewall(PF_INET, skb, skb->h.iph);
+		switch (fw_res) {
+		case FW_ACCEPT:
+		case FW_MASQUERADE:
+			break;
+		case FW_REJECT:
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
+			/* fall thru */
+		default:
+			return -1;
+		}
+	}
+#endif
+	/*
+	 *	According to the RFC, we must first decrease the TTL field. If
+	 *	that reaches zero, we must reply an ICMP control message telling
+	 *	that the packet's lifetime expired.
+	 *
+	 *	Exception:
+	 *	We may not generate an ICMP for an ICMP. icmp_send does the
+	 *	enforcement of this so we can forget it here. It is however
+	 *	sometimes VERY important.
+	 */
+
+	iph = skb->h.iph;
+	iph->ttl--;
+
+	/*
+	 *	Re-compute the IP header checksum.
+	 *	This is inefficient. We know what has happened to the header
+	 *	and could thus adjust the checksum as Phil Karn does in KA9Q
+	 */
+
+	iph->check = ntohs(iph->check) + 0x0100;
+	if ((iph->check & 0xFF00) == 0)
+		iph->check++;		/* carry overflow */
+	iph->check = htons(iph->check);
+
+	if (iph->ttl <= 0)
+	{
+		/* Tell the sender its packet died... */
+		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
+		return -1;
+	}
+
+#ifdef CONFIG_IP_MROUTE
+	if(!(is_frag&8))
+	{
+#endif	
+		/*
+		 * OK, the packet is still valid.  Fetch its destination address,
+		 * and give it to the IP sender for further processing.
+		 */
+
+		rt = ip_rt_route(target_addr, NULL, NULL);
+		if (rt == NULL)
+		{
+			/*
+			 *	Tell the sender its packet cannot be delivered. Again
+			 *	ICMP is screened later.
+			 */
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);
+			return -1;
+		}
+	
+	
+		/*
+		 * Gosh.  Not only is the packet valid; we even know how to
+		 * forward it onto its final destination.  Can we say this
+		 * is being plain lucky?
+		 * If the router told us that there is no GW, use the dest.
+		 * IP address itself- we seem to be connected directly...
+		 */
+
+		raddr = rt->rt_gateway;
+	
+		if (raddr != 0)
+		{
+			/*
+			 *	Strict routing permits no gatewaying
+			 */
+	
+		        if (opt->is_strictroute)
+			{
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
+				return -1;
+			}
+		
+			/*
+			 *	There is a gateway so find the correct route for it.
+			 *	Gateways cannot in turn be gatewayed.
+			 */
+		}
+		else
+			raddr = target_addr;
+
+		/*
+		 *	Having picked a route we can now send the frame out.
+		 */
+
+		dev2 = rt->rt_dev;
+		/*
+		 *	In IP you never have to forward a frame on the interface that it 
+		 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
+		 *	we calculated.
+		 */
+#ifndef CONFIG_IP_NO_ICMP_REDIRECT
+		if (dev == dev2 && !((iph->saddr^iph->daddr)&dev->pa_mask) &&
+		    (rt->rt_flags&RTF_MODIFIED) && !opt->srr)
+			icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev);
+#endif
+#ifdef CONFIG_IP_MROUTE
+	}
+	else
+	{
+		/*
+		 *	Multicast route forward. Routing is already done
+		 */
+		dev2=skb->dev;
+		raddr=skb->raddr;
+		if(is_frag&16)		/* VIFF_TUNNEL mode */
+			encap=20;
+	}
+#endif	
+	
+
+	/*
+	 * We now may allocate a new buffer, and copy the datagram into it.
+	 * If the indicated interface is up and running, kick it.
+	 */
+
+	if (dev2->flags & IFF_UP)
+	{
+#ifdef CONFIG_IP_MASQUERADE
+		/*
+		 * If this fragment needs masquerading, make it so...
+		 * (Dont masquerade de-masqueraded fragments)
+		 */
+		if (!(is_frag&4) && fw_res==FW_MASQUERADE)
+			ip_fw_masquerade(&skb, dev2);
+#endif
+		IS_SKB(skb);
+
+		if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+		  ip_statistics.IpFragFails++;
+		  icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev2->mtu, dev);
+		  return -1;
+		}
+
+#ifdef CONFIG_IP_MROUTE
+		if(skb_headroom(skb)-encap<dev2->hard_header_len)
+		{
+			skb2 = alloc_skb(dev2->hard_header_len + skb->len + encap + 15, GFP_ATOMIC);
+#else
+		if(skb_headroom(skb)<dev2->hard_header_len)
+		{
+			skb2 = alloc_skb(dev2->hard_header_len + skb->len + 15, GFP_ATOMIC);
+#endif		
+			/*
+			 *	This is rare and since IP is tolerant of network failures
+			 *	quite harmless.
+			 */
+		
+			if (skb2 == NULL)
+			{
+				NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
+				return -1;
+			}
+		
+			IS_SKB(skb2);
+			/*
+			 *	Add the physical headers.
+			 */
+#ifdef CONFIG_IP_MROUTE
+			if(is_frag&16)
+			{
+				skb_reserve(skb,(encap+dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
+				ip_encap(skb2,skb->len, dev2, raddr);
+			}
+			else
+#endif			
+		 		ip_send(skb2,raddr,skb->len,dev2,dev2->pa_addr);
+
+			/*
+			 *	We have to copy the bytes over as the new header wouldn't fit
+			 *	the old buffer. This should be very rare.
+			 */		 
+		 	
+			ptr = skb_put(skb2,skb->len);
+			skb2->free = 1;
+			skb2->h.raw = ptr;
+
+			/*
+			 *	Copy the packet data into the new buffer.
+			 */
+			memcpy(ptr, skb->h.raw, skb->len);
+			memcpy(skb2->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+			iph = skb2->ip_hdr = skb2->h.iph;
+		}
+		else
+		{
+			/* 
+			 *	Build a new MAC header. 
+			 */
+
+			skb2 = skb;		
+			skb2->dev=dev2;
+#ifdef CONFIG_IP_MROUTE
+			if(is_frag&16)
+				ip_encap(skb,skb->len, dev2, raddr);
+			else
+			{
+#endif
+				skb->arp=1;
+				skb->raddr=raddr;
+				if(dev2->hard_header)
+				{
+					if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0)
+						skb->arp=0;
+				}
+#ifdef CONFIG_IP_MROUTE
+			}				
+#endif			
+			ip_statistics.IpForwDatagrams++;
+		}
+
+		if (opt->optlen) 
+		{
+			unsigned char * optptr;
+			if (opt->rr_needaddr) 
+			{
+				optptr = (unsigned char *)iph + opt->rr;
+				memcpy(&optptr[optptr[2]-5], &dev2->pa_addr, 4);
+				opt->is_changed = 1;
+			}
+			if (opt->srr_is_hit) 
+			{
+				int srrptr, srrspace;
+
+				optptr = (unsigned char *)iph + opt->srr;
+
+				for ( srrptr=optptr[2], srrspace = optptr[1];
+				      srrptr <= srrspace;
+				     srrptr += 4
+				    ) 
+				{
+					if (srrptr + 3 > srrspace)
+						break;
+					if (memcmp(&target_addr, &optptr[srrptr-1], 4) == 0)
+						break;
+				}
+				if (srrptr + 3 <= srrspace) 
+				{
+					opt->is_changed = 1;
+					memcpy(&optptr[srrptr-1], &dev2->pa_addr, 4);
+					iph->daddr = target_addr;
+					optptr[2] = srrptr+4;
+				}
+				else
+				        printk("ip_forward(): Argh! Destination lost!\n");
+			}
+			if (opt->ts_needaddr) 
+			{
+				optptr = (unsigned char *)iph + opt->ts;
+				memcpy(&optptr[optptr[2]-9], &dev2->pa_addr, 4);
+				opt->is_changed = 1;
+			}
+			if (opt->is_changed) 
+			{
+				opt->is_changed = 0;
+				ip_send_check(iph);
+			}
+		}
+/*
+ * ANK:  this is point of "no return", we cannot send an ICMP,
+ *       because we changed SRR option.
+ */
+
+		/*
+		 *	See if it needs fragmenting. Note in ip_rcv we tagged
+		 *	the fragment type. This must be right so that
+		 *	the fragmenter does the right thing.
+		 */
+
+		if(skb2->len > dev2->mtu + dev2->hard_header_len)
+		{
+			ip_fragment(NULL,skb2,dev2, is_frag);
+			kfree_skb(skb2,FREE_WRITE);
+		}
+		else
+		{
+#ifdef CONFIG_IP_ACCT		
+			/*
+			 *	Count mapping we shortcut
+			 */
+			 
+			ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
+#endif			
+			
+			/*
+			 *	Map service types to priority. We lie about
+			 *	throughput being low priority, but it's a good
+			 *	choice to help improve general usage.
+			 */
+			if(iph->tos & IPTOS_LOWDELAY)
+				dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
+			else if(iph->tos & IPTOS_THROUGHPUT)
+				dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
+			else
+				dev_queue_xmit(skb2, dev2, SOPRI_NORMAL);
+		}
+	}
+	else
+		return -1;
+	
+	/*
+	 *	Tell the caller if their buffer is free.
+	 */	 
+	 
+	if(skb==skb2)
+		return 0;	
+
+#ifdef CONFIG_IP_MASQUERADE	
+	/*
+	 *	The original is free. Free our copy and
+	 *	tell the caller not to free.
+	 */
+	if(skb!=skb_in)
+	{
+		kfree_skb(skb_in, FREE_WRITE);
+		return 0;
+	}
+#endif	
+	return 1;
+}
+
+
+#endif
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v1.3.35/linux/net/ipv4/ip_fragment.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/ip_fragment.c	Tue Oct 17 13:42:37 1995
@@ -0,0 +1,711 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The IP fragmentation functionality.
+ *		
+ * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
+ *		Alan Cox <Alan.Cox@linux.org>
+ *
+ * Fixes:
+ *		Alan Cox	:	Split from ip.c , see ip_input.c for history.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#include <net/checksum.h>
+
+/*
+ *	This fragment handler is a bit of a heap. On the other hand it works quite
+ *	happily and handles things quite well.
+ */
+
+static struct ipq *ipqueue = NULL;		/* IP fragment queue	*/
+
+/*
+ *	Create a new fragment entry.
+ */
+
+static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
+{
+	struct ipfrag *fp;
+
+	fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
+	if (fp == NULL)
+	{
+		NETDEBUG(printk("IP: frag_create: no memory left !\n"));
+		return(NULL);
+	}
+	memset(fp, 0, sizeof(struct ipfrag));
+
+	/* Fill in the structure. */
+	fp->offset = offset;
+	fp->end = end;
+	fp->len = end - offset;
+	fp->skb = skb;
+	fp->ptr = ptr;
+
+	return(fp);
+}
+
+
+/*
+ *	Find the correct entry in the "incomplete datagrams" queue for
+ *	this IP datagram, and return the queue entry address if found.
+ */
+
+static struct ipq *ip_find(struct iphdr *iph)
+{
+	struct ipq *qp;
+	struct ipq *qplast;
+
+	cli();
+	qplast = NULL;
+	for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next)
+	{
+		if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr &&
+			iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol)
+		{
+			del_timer(&qp->timer);	/* So it doesn't vanish on us. The timer will be reset anyway */
+			sti();
+			return(qp);
+		}
+	}
+	sti();
+	return(NULL);
+}
+
+
+/*
+ *	Remove an entry from the "incomplete datagrams" queue, either
+ *	because we completed, reassembled and processed it, or because
+ *	it timed out.
+ */
+
+static void ip_free(struct ipq *qp)
+{
+	struct ipfrag *fp;
+	struct ipfrag *xp;
+
+	/*
+	 * Stop the timer for this entry.
+	 */
+
+	del_timer(&qp->timer);
+
+	/* Remove this entry from the "incomplete datagrams" queue. */
+	cli();
+	if (qp->prev == NULL)
+	{
+		ipqueue = qp->next;
+		if (ipqueue != NULL)
+			ipqueue->prev = NULL;
+	}
+	else
+	{
+		qp->prev->next = qp->next;
+		if (qp->next != NULL)
+			qp->next->prev = qp->prev;
+	}
+
+	/* Release all fragment data. */
+
+	fp = qp->fragments;
+	while (fp != NULL)
+	{
+		xp = fp->next;
+		IS_SKB(fp->skb);
+		kfree_skb(fp->skb,FREE_READ);
+		kfree_s(fp, sizeof(struct ipfrag));
+		fp = xp;
+	}
+
+	/* Release the IP header. */
+	kfree_s(qp->iph, 64 + 8);
+
+	/* Finally, release the queue descriptor itself. */
+	kfree_s(qp, sizeof(struct ipq));
+	sti();
+}
+
+
+/*
+ *	Oops- a fragment queue timed out.  Kill it and send an ICMP reply.
+ */
+
+static void ip_expire(unsigned long arg)
+{
+	struct ipq *qp;
+
+	qp = (struct ipq *)arg;
+
+	/*
+	 *	Send an ICMP "Fragment Reassembly Timeout" message.
+	 */
+
+	ip_statistics.IpReasmTimeout++;
+	ip_statistics.IpReasmFails++;   
+	/* This if is always true... shrug */
+	if(qp->fragments!=NULL)
+		icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
+				ICMP_EXC_FRAGTIME, 0, qp->dev);
+
+	/*
+	 *	Nuke the fragment queue.
+	 */
+	ip_free(qp);
+}
+
+
+/*
+ * 	Add an entry to the 'ipq' queue for a newly received IP datagram.
+ * 	We will (hopefully :-) receive all other fragments of this datagram
+ * 	in time, so we just create a queue for this datagram, in which we
+ * 	will insert the received fragments at their respective positions.
+ */
+
+static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
+{
+	struct ipq *qp;
+	int ihlen;
+
+	qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
+	if (qp == NULL)
+	{
+		NETDEBUG(printk("IP: create: no memory left !\n"));
+		return(NULL);
+		skb->dev = qp->dev;
+	}
+	memset(qp, 0, sizeof(struct ipq));
+
+	/*
+	 *	Allocate memory for the IP header (plus 8 octets for ICMP).
+	 */
+
+	ihlen = iph->ihl * 4;
+	qp->iph = (struct iphdr *) kmalloc(64 + 8, GFP_ATOMIC);
+	if (qp->iph == NULL)
+	{
+		NETDEBUG(printk("IP: create: no memory left !\n"));
+		kfree_s(qp, sizeof(struct ipq));
+		return(NULL);
+	}
+
+	memcpy(qp->iph, iph, ihlen + 8);
+	qp->len = 0;
+	qp->ihlen = ihlen;
+	qp->fragments = NULL;
+	qp->dev = dev;
+
+	/* Start a timer for this entry. */
+	qp->timer.expires = jiffies + IP_FRAG_TIME;	/* about 30 seconds	*/
+	qp->timer.data = (unsigned long) qp;		/* pointer to queue	*/
+	qp->timer.function = ip_expire;			/* expire function	*/
+	add_timer(&qp->timer);
+
+	/* Add this entry to the queue. */
+	qp->prev = NULL;
+	cli();
+	qp->next = ipqueue;
+	if (qp->next != NULL)
+		qp->next->prev = qp;
+	ipqueue = qp;
+	sti();
+	return(qp);
+}
+
+
+/*
+ *	See if a fragment queue is complete.
+ */
+
+static int ip_done(struct ipq *qp)
+{
+	struct ipfrag *fp;
+	int offset;
+
+	/* Only possible if we received the final fragment. */
+	if (qp->len == 0)
+		return(0);
+
+	/* Check all fragment offsets to see if they connect. */
+	fp = qp->fragments;
+	offset = 0;
+	while (fp != NULL)
+	{
+		if (fp->offset > offset)
+			return(0);	/* fragment(s) missing */
+		offset = fp->end;
+		fp = fp->next;
+	}
+
+	/* All fragments are present. */
+	return(1);
+}
+
+
+/*
+ *	Build a new IP datagram from all its fragments.
+ *
+ *	FIXME: We copy here because we lack an effective way of handling lists
+ *	of bits on input. Until the new skb data handling is in I'm not going
+ *	to touch this with a bargepole. 
+ */
+
+static struct sk_buff *ip_glue(struct ipq *qp)
+{
+	struct sk_buff *skb;
+	struct iphdr *iph;
+	struct ipfrag *fp;
+	unsigned char *ptr;
+	int count, len;
+
+	/*
+	 *	Allocate a new buffer for the datagram.
+	 */
+	len = qp->ihlen + qp->len;
+
+	if ((skb = dev_alloc_skb(len)) == NULL)
+	{
+		ip_statistics.IpReasmFails++;
+		NETDEBUG(printk("IP: queue_glue: no memory for gluing queue %p\n", qp));
+		ip_free(qp);
+		return(NULL);
+	}
+
+	/* Fill in the basic details. */
+	skb_put(skb,len);
+	skb->h.raw = skb->data;
+	skb->free = 1;
+
+	/* Copy the original IP headers into the new buffer. */
+	ptr = (unsigned char *) skb->h.raw;
+	memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
+	ptr += qp->ihlen;
+
+	count = 0;
+
+	/* Copy the data portions of all fragments into the new buffer. */
+	fp = qp->fragments;
+	while(fp != NULL)
+	{
+		if(count+fp->len > skb->len)
+		{
+			NETDEBUG(printk("Invalid fragment list: Fragment over size.\n"));
+			ip_free(qp);
+			kfree_skb(skb,FREE_WRITE);
+			ip_statistics.IpReasmFails++;
+			return NULL;
+		}
+		memcpy((ptr + fp->offset), fp->ptr, fp->len);
+		count += fp->len;
+		fp = fp->next;
+	}
+
+	/* We glued together all fragments, so remove the queue entry. */
+	ip_free(qp);
+
+	/* Done with all fragments. Fixup the new IP header. */
+	iph = skb->h.iph;
+	iph->frag_off = 0;
+	iph->tot_len = htons((iph->ihl * 4) + count);
+	skb->ip_hdr = iph;
+
+	ip_statistics.IpReasmOKs++;
+	return(skb);
+}
+
+
+/*
+ *	Process an incoming IP datagram fragment.
+ */
+
+struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
+{
+	struct ipfrag *prev, *next, *tmp;
+	struct ipfrag *tfp;
+	struct ipq *qp;
+	struct sk_buff *skb2;
+	unsigned char *ptr;
+	int flags, offset;
+	int i, ihl, end;
+
+	ip_statistics.IpReasmReqds++;
+
+	/* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
+	qp = ip_find(iph);
+
+	/* Is this a non-fragmented datagram? */
+	offset = ntohs(iph->frag_off);
+	flags = offset & ~IP_OFFSET;
+	offset &= IP_OFFSET;
+	if (((flags & IP_MF) == 0) && (offset == 0))
+	{
+		if (qp != NULL)
+			ip_free(qp);	/* Huh? How could this exist?? */
+		return(skb);
+	}
+
+	offset <<= 3;		/* offset is in 8-byte chunks */
+	ihl = iph->ihl * 4;
+
+	/*
+	 * If the queue already existed, keep restarting its timer as long
+	 * as we still are receiving fragments.  Otherwise, create a fresh
+	 * queue entry.
+	 */
+
+	if (qp != NULL)
+	{
+		/* ANK. If the first fragment is received,
+		 * we should remember the correct IP header (with options)
+		 */
+	        if (offset == 0)
+		{
+			qp->ihlen = ihl;
+			memcpy(qp->iph, iph, ihl+8);
+		}
+		del_timer(&qp->timer);
+		qp->timer.expires = jiffies + IP_FRAG_TIME;	/* about 30 seconds */
+		qp->timer.data = (unsigned long) qp;	/* pointer to queue */
+		qp->timer.function = ip_expire;		/* expire function */
+		add_timer(&qp->timer);
+	}
+	else
+	{
+		/*
+		 *	If we failed to create it, then discard the frame
+		 */
+		if ((qp = ip_create(skb, iph, dev)) == NULL)
+		{
+			skb->sk = NULL;
+			kfree_skb(skb, FREE_READ);
+			ip_statistics.IpReasmFails++;
+			return NULL;
+		}
+	}
+
+	/*
+	 *	Determine the position of this fragment.
+	 */
+
+	end = offset + ntohs(iph->tot_len) - ihl;
+
+	/*
+	 *	Point into the IP datagram 'data' part.
+	 */
+
+	ptr = skb->data + ihl;
+
+	/*
+	 *	Is this the final fragment?
+	 */
+
+	if ((flags & IP_MF) == 0)
+		qp->len = end;
+
+	/*
+	 * 	Find out which fragments are in front and at the back of us
+	 * 	in the chain of fragments so far.  We must know where to put
+	 * 	this fragment, right?
+	 */
+
+	prev = NULL;
+	for(next = qp->fragments; next != NULL; next = next->next)
+	{
+		if (next->offset > offset)
+			break;	/* bingo! */
+		prev = next;
+	}
+
+	/*
+	 * 	We found where to put this one.
+	 * 	Check for overlap with preceding fragment, and, if needed,
+	 * 	align things so that any overlaps are eliminated.
+	 */
+	if (prev != NULL && offset < prev->end)
+	{
+		i = prev->end - offset;
+		offset += i;	/* ptr into datagram */
+		ptr += i;	/* ptr into fragment data */
+	}
+
+	/*
+	 * Look for overlap with succeeding segments.
+	 * If we can merge fragments, do it.
+	 */
+
+	for(tmp=next; tmp != NULL; tmp = tfp)
+	{
+		tfp = tmp->next;
+		if (tmp->offset >= end)
+			break;		/* no overlaps at all */
+
+		i = end - next->offset;			/* overlap is 'i' bytes */
+		tmp->len -= i;				/* so reduce size of	*/
+		tmp->offset += i;			/* next fragment	*/
+		tmp->ptr += i;
+		/*
+		 *	If we get a frag size of <= 0, remove it and the packet
+		 *	that it goes with.
+		 */
+		if (tmp->len <= 0)
+		{
+			if (tmp->prev != NULL)
+				tmp->prev->next = tmp->next;
+			else
+				qp->fragments = tmp->next;
+
+			if (tfp->next != NULL)
+				tmp->next->prev = tmp->prev;
+			
+			next=tfp;	/* We have killed the original next frame */
+
+			kfree_skb(tmp->skb,FREE_READ);
+			kfree_s(tmp, sizeof(struct ipfrag));
+		}
+	}
+
+	/*
+	 *	Insert this fragment in the chain of fragments.
+	 */
+
+	tfp = NULL;
+	tfp = ip_frag_create(offset, end, skb, ptr);
+
+	/*
+	 *	No memory to save the fragment - so throw the lot
+	 */
+
+	if (!tfp)
+	{
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_READ);
+		return NULL;
+	}
+	tfp->prev = prev;
+	tfp->next = next;
+	if (prev != NULL)
+		prev->next = tfp;
+	else
+		qp->fragments = tfp;
+
+	if (next != NULL)
+		next->prev = tfp;
+
+	/*
+	 * 	OK, so we inserted this new fragment into the chain.
+	 * 	Check if we now have a full IP datagram which we can
+	 * 	bump up to the IP layer...
+	 */
+
+	if (ip_done(qp))
+	{
+		skb2 = ip_glue(qp);		/* glue together the fragments */
+		return(skb2);
+	}
+	return(NULL);
+}
+
+
+/*
+ *	This IP datagram is too large to be sent in one piece.  Break it up into
+ *	smaller pieces (each of size equal to the MAC header plus IP header plus
+ *	a block of the data of the original IP data part) that will yet fit in a
+ *	single device frame, and queue such a frame for sending by calling the
+ *	ip_queue_xmit().  Note that this is recursion, and bad things will happen
+ *	if this function causes a loop...
+ *
+ *	Yes this is inefficient, feel free to submit a quicker one.
+ *
+ */
+ 
+void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
+{
+	struct iphdr *iph;
+	unsigned char *raw;
+	unsigned char *ptr;
+	struct sk_buff *skb2;
+	int left, mtu, hlen, len;
+	int offset;
+	unsigned long flags;
+
+	/*
+	 *	Point into the IP datagram header.
+	 */
+
+	raw = skb->data;
+#if 0
+	iph = (struct iphdr *) (raw + dev->hard_header_len);	
+	skb->ip_hdr = iph;
+#else
+	iph = skb->ip_hdr;
+#endif
+
+	/*
+	 *	Setup starting values.
+	 */
+
+	hlen = iph->ihl * 4;
+	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
+	hlen += dev->hard_header_len;		/* Total header size */
+	mtu = (dev->mtu - hlen);		/* Size of data space */
+	ptr = (raw + hlen);			/* Where to start from */
+
+	/*
+	 *	Check for any "DF" flag. [DF means do not fragment]
+	 */
+
+	if (ntohs(iph->frag_off) & IP_DF)
+	{
+		ip_statistics.IpFragFails++;
+		printk("ip_queue_xmit: frag needed\n");
+		return;
+	}
+
+	/*
+	 *	The protocol doesn't seem to say what to do in the case that the
+	 *	frame + options doesn't fit the mtu. As it used to fall down dead
+	 *	in this case we were fortunate it didn't happen
+	 */
+
+	if(mtu<8)
+	{
+		/* It's wrong but it's better than nothing */
+		icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev);
+		ip_statistics.IpFragFails++;
+		return;
+	}
+
+	/*
+	 *	Fragment the datagram.
+	 */
+
+	/*
+	 *	The initial offset is 0 for a complete frame. When
+	 *	fragmenting fragments it's wherever this one starts.
+	 */
+
+	if (is_frag & 2)
+		offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+	else
+		offset = 0;
+
+
+	/*
+	 *	Keep copying data until we run out.
+	 */
+
+	while(left > 0)
+	{
+		len = left;
+		/* IF: it doesn't fit, use 'mtu' - the data space left */
+		if (len > mtu)
+			len = mtu;
+		/* IF: we are not sending upto and including the packet end
+		   then align the next start on an eight byte boundary */
+		if (len < left)
+		{
+			len/=8;
+			len*=8;
+		}
+		/*
+		 *	Allocate buffer.
+		 */
+
+		if ((skb2 = alloc_skb(len + hlen+15,GFP_ATOMIC)) == NULL)
+		{
+			NETDEBUG(printk("IP: frag: no memory for new fragment!\n"));
+			ip_statistics.IpFragFails++;
+			return;
+		}
+
+		/*
+		 *	Set up data on packet
+		 */
+
+		skb2->arp = skb->arp;
+		if(skb->free==0)
+			printk("IP fragmenter: BUG free!=1 in fragmenter\n");
+		skb2->free = 1;
+		skb_put(skb2,len + hlen);
+		skb2->h.raw=(char *) skb2->data;
+		/*
+		 *	Charge the memory for the fragment to any owner
+		 *	it might possess
+		 */
+
+		save_flags(flags);
+		if (sk)
+		{
+			cli();
+			sk->wmem_alloc += skb2->truesize;
+			skb2->sk=sk;
+		}
+		restore_flags(flags);
+		skb2->raddr = skb->raddr;	/* For rebuild_header - must be here */
+
+		/*
+		 *	Copy the packet header into the new buffer.
+		 */
+
+		memcpy(skb2->h.raw, raw, hlen);
+
+		/*
+		 *	Copy a block of the IP datagram.
+		 */
+		memcpy(skb2->h.raw + hlen, ptr, len);
+		left -= len;
+
+		skb2->h.raw+=dev->hard_header_len;
+
+		/*
+		 *	Fill in the new header fields.
+		 */
+		iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
+		iph->frag_off = htons((offset >> 3));
+		skb2->ip_hdr = iph;
+
+		/* ANK: dirty, but effective trick. Upgrade options only if
+		 * the segment to be fragmented was THE FIRST (otherwise,
+		 * options are already fixed) and make it ONCE
+		 * on the initial skb, so that all the following fragments
+		 * will inherit fixed options.
+		 */
+		if (offset == 0)
+			ip_options_fragment(skb);
+
+		/*
+		 *	Added AC : If we are fragmenting a fragment thats not the
+		 *		   last fragment then keep MF on each bit
+		 */
+		if (left > 0 || (is_frag & 1))
+			iph->frag_off |= htons(IP_MF);
+		ptr += len;
+		offset += len;
+
+		/*
+		 *	Put this fragment into the sending queue.
+		 */
+
+		ip_statistics.IpFragCreates++;
+
+		ip_queue_xmit(sk, dev, skb2, 2);
+	}
+	ip_statistics.IpFragOKs++;
+}
+
+
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v1.3.35/linux/net/ipv4/ip_fw.c	Tue Oct 10 18:46:39 1995
+++ linux/net/ipv4/ip_fw.c	Wed Oct 18 07:24:19 1995
@@ -87,6 +87,7 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/icmp.h>
+#include <linux/firewall.h>
 #include <linux/ip_fw.h>
 #include <net/checksum.h>
 #include <linux/proc_fs.h>
@@ -1668,6 +1669,33 @@
   
 #endif
 
+#ifdef CONFIG_IP_FIREWALL
+/*
+ *	Interface to the generic firewall chains.
+ */
+ 
+int ipfw_input_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
+{
+	return ip_fw_chk(phdr, skb->dev, ip_fw_blk_chain, ip_fw_blk_policy, 0);
+}
+
+int ipfw_forward_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
+{
+	return ip_fw_chk(phdr, skb->dev, ip_fw_fwd_chain, ip_fw_fwd_policy, 0);
+}
+ 
+struct firewall_ops ipfw_ops=
+{
+	NULL,
+	ipfw_forward_check,
+	ipfw_input_check,
+	ipfw_input_check,
+	PF_INET,
+	0	/* We don't even allow a fall through so we are last */
+};
+
+#endif
+
 void ip_fw_init(void)
 {
 #ifdef CONFIG_IP_ACCT
@@ -1679,6 +1707,10 @@
 	});
 #endif
 #ifdef CONFIG_IP_FIREWALL
+
+	if(register_firewall(PF_INET,&ipfw_ops)<0)
+		panic("Unable to register IP firewall.\n");
+		
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_IPFWBLK, 8, "ip_block",
 		S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v1.3.35/linux/net/ipv4/ip_input.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/ip_input.c	Tue Oct 17 13:42:37 1995
@@ -0,0 +1,613 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The Internet Protocol (IP) module.
+ *
+ * Version:	@(#)ip.c	1.0.16b	9/1/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald Becker, <becker@super.org>
+ *		Alan Cox, <Alan.Cox@linux.org>
+ *		Richard Underwood
+ *		Stefan Becker, <stefanb@yello.ping.de>
+ *		Jorge Cwik, <jorge@laser.satlink.net>
+ *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *		
+ *
+ * Fixes:
+ *		Alan Cox	:	Commented a couple of minor bits of surplus code
+ *		Alan Cox	:	Undefining IP_FORWARD doesn't include the code
+ *					(just stops a compiler warning).
+ *		Alan Cox	:	Frames with >=MAX_ROUTE record routes, strict routes or loose routes
+ *					are junked rather than corrupting things.
+ *		Alan Cox	:	Frames to bad broadcast subnets are dumped
+ *					We used to process them non broadcast and
+ *					boy could that cause havoc.
+ *		Alan Cox	:	ip_forward sets the free flag on the
+ *					new frame it queues. Still crap because
+ *					it copies the frame but at least it
+ *					doesn't eat memory too.
+ *		Alan Cox	:	Generic queue code and memory fixes.
+ *		Fred Van Kempen :	IP fragment support (borrowed from NET2E)
+ *		Gerhard Koerting:	Forward fragmented frames correctly.
+ *		Gerhard Koerting: 	Fixes to my fix of the above 8-).
+ *		Gerhard Koerting:	IP interface addressing fix.
+ *		Linus Torvalds	:	More robustness checks
+ *		Alan Cox	:	Even more checks: Still not as robust as it ought to be
+ *		Alan Cox	:	Save IP header pointer for later
+ *		Alan Cox	:	ip option setting
+ *		Alan Cox	:	Use ip_tos/ip_ttl settings
+ *		Alan Cox	:	Fragmentation bogosity removed
+ *					(Thanks to Mark.Bush@prg.ox.ac.uk)
+ *		Dmitry Gorodchanin :	Send of a raw packet crash fix.
+ *		Alan Cox	:	Silly ip bug when an overlength
+ *					fragment turns up. Now frees the
+ *					queue.
+ *		Linus Torvalds/ :	Memory leakage on fragmentation
+ *		Alan Cox	:	handling.
+ *		Gerhard Koerting:	Forwarding uses IP priority hints
+ *		Teemu Rantanen	:	Fragment problems.
+ *		Alan Cox	:	General cleanup, comments and reformat
+ *		Alan Cox	:	SNMP statistics
+ *		Alan Cox	:	BSD address rule semantics. Also see
+ *					UDP as there is a nasty checksum issue
+ *					if you do things the wrong way.
+ *		Alan Cox	:	Always defrag, moved IP_FORWARD to the config.in file
+ *		Alan Cox	: 	IP options adjust sk->priority.
+ *		Pedro Roque	:	Fix mtu/length error in ip_forward.
+ *		Alan Cox	:	Avoid ip_chk_addr when possible.
+ *	Richard Underwood	:	IP multicasting.
+ *		Alan Cox	:	Cleaned up multicast handlers.
+ *		Alan Cox	:	RAW sockets demultiplex in the BSD style.
+ *		Gunther Mayer	:	Fix the SNMP reporting typo
+ *		Alan Cox	:	Always in group 224.0.0.1
+ *	Pauline Middelink	:	Fast ip_checksum update when forwarding
+ *					Masquerading support.
+ *		Alan Cox	:	Multicast loopback error for 224.0.0.1
+ *		Alan Cox	:	IP_MULTICAST_LOOP option.
+ *		Alan Cox	:	Use notifiers.
+ *		Bjorn Ekwall	:	Removed ip_csum (from slhc.c too)
+ *		Bjorn Ekwall	:	Moved ip_fast_csum to ip.h (inline!)
+ *		Stefan Becker   :       Send out ICMP HOST REDIRECT
+ *	Arnt Gulbrandsen	:	ip_build_xmit
+ *		Alan Cox	:	Per socket routing cache
+ *		Alan Cox	:	Fixed routing cache, added header cache.
+ *		Alan Cox	:	Loopback didnt work right in original ip_build_xmit - fixed it.
+ *		Alan Cox	:	Only send ICMP_REDIRECT if src/dest are the same net.
+ *		Alan Cox	:	Incoming IP option handling.
+ *		Alan Cox	:	Set saddr on raw output frames as per BSD.
+ *		Alan Cox	:	Stopped broadcast source route explosions.
+ *		Alan Cox	:	Can disable source routing
+ *		Takeshi Sone    :	Masquerading didn't work.
+ *	Dave Bonn,Alan Cox	:	Faster IP forwarding whenever possible.
+ *		Alan Cox	:	Memory leaks, tramples, misc debugging.
+ *		Alan Cox	:	Fixed multicast (by popular demand 8))
+ *		Alan Cox	:	Fixed forwarding (by even more popular demand 8))
+ *		Alan Cox	:	Fixed SNMP statistics [I think]
+ *	Gerhard Koerting	:	IP fragmentation forwarding fix
+ *		Alan Cox	:	Device lock against page fault.
+ *		Alan Cox	:	IP_HDRINCL facility.
+ *	Werner Almesberger	:	Zero fragment bug
+ *		Alan Cox	:	RAW IP frame length bug
+ *		Alan Cox	:	Outgoing firewall on build_xmit
+ *		A.N.Kuznetsov	:	IP_OPTIONS support throughout the kernel
+ *		Alan Cox	:	Multicast routing hooks
+ *
+ *  
+ *
+ * To Fix:
+ *		IP fragmentation wants rewriting cleanly. The RFC815 algorithm is much more efficient
+ *		and could be made very efficient with the addition of some virtual memory hacks to permit
+ *		the allocation of a buffer that can then be 'grown' by twiddling page tables.
+ *		Output fragmentation wants updating along with the buffer management to use a single 
+ *		interleaved copy algorithm so that fragmenting has a one copy overhead. Actual packet
+ *		output should probably do its own fragmentation at the UDP/RAW layer. TCP shouldn't cause
+ *		fragmentation anyway.
+ *
+ *		FIXME: copy frag 0 iph to qp->iph
+ *
+ *		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.
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/icmp.h>
+#include <net/raw.h>
+#include <net/checksum.h>
+#include <linux/igmp.h>
+#include <linux/ip_fw.h>
+#include <linux/firewall.h>
+#include <linux/mroute.h>
+#include <net/netlink.h>
+
+extern int last_retran;
+extern void sort_send(struct sock *sk);
+
+#define min(a,b)	((a)<(b)?(a):(b))
+
+/*
+ *	SNMP management statistics
+ */
+
+#ifdef CONFIG_IP_FORWARD
+struct ip_mib ip_statistics={1,64,};	/* Forwarding=Yes, Default TTL=64 */
+#else
+struct ip_mib ip_statistics={2,64,};	/* Forwarding=No, Default TTL=64 */
+#endif
+
+/*
+ *	Handle the issuing of an ioctl() request
+ *	for the ip device. This is scheduled to
+ *	disappear
+ */
+
+int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+	switch(cmd)
+	{
+		default:
+			return(-EINVAL);
+	}
+}
+
+
+
+/*
+ *	This function receives all incoming IP datagrams.
+ *
+ *	On entry skb->data points to the start of the IP header and
+ *	the MAC header has been removed.
+ */
+
+int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	struct iphdr *iph = skb->h.iph;
+	struct sock *raw_sk=NULL;
+	unsigned char hash;
+	unsigned char flag = 0;
+	struct inet_protocol *ipprot;
+	int brd=IS_MYADDR;
+	struct options * opt = NULL;
+	int is_frag=0;
+#ifdef CONFIG_FIREWALL
+	int err;
+#endif	
+#ifdef CONFIG_IP_MROUTE
+	int mroute_pkt=0;
+#endif	
+
+#ifdef CONFIG_NET_IPV6
+	/* 
+	 *	Intercept IPv6 frames. We dump ST-II and invalid types just below..
+	 */
+	 
+	if(iph->version == 6)
+		return ipv6_rcv(skb,dev,pt);
+#endif		
+
+	ip_statistics.IpInReceives++;
+
+	/*
+	 *	Tag the ip header of this packet so we can find it
+	 */
+
+	skb->ip_hdr = iph;
+
+	/*
+	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
+	 *	RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING].
+	 *
+	 *	Is the datagram acceptable?
+	 *
+	 *	1.	Length at least the size of an ip header
+	 *	2.	Version of 4
+	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
+	 *	4.	Doesn't have a bogus length
+	 *	(5.	We ought to check for IP multicast addresses and undefined types.. does this matter ?)
+	 */
+
+	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
+		|| skb->len < ntohs(iph->tot_len))
+	{
+		ip_statistics.IpInHdrErrors++;
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
+	}
+
+	/*
+	 *	Our transport medium may have padded the buffer out. Now we know it
+	 *	is IP we can trim to the true length of the frame.
+	 *	Note this now means skb->len holds ntohs(iph->tot_len).
+	 */
+
+	skb_trim(skb,ntohs(iph->tot_len));
+
+	if (iph->ihl > 5) 
+	{
+		skb->ip_summed = 0;
+		if (ip_options_compile(NULL, skb))
+			return(0);
+		opt = (struct options*)skb->proto_priv;
+#ifdef CONFIG_IP_NOSR
+		if (opt->srr) 
+		{
+			kfree_skb(skb, FREE_READ);
+			return -EINVAL;
+		}
+#endif					
+	}
+	
+	/*
+	 *	See if the firewall wants to dispose of the packet. 
+	 */
+
+#ifdef	CONFIG_FIREWALL
+	
+	if ((err=call_in_firewall(PF_INET, skb, iph))<FW_ACCEPT)
+	{
+		if(err==FW_REJECT)
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
+		kfree_skb(skb, FREE_WRITE);
+		return 0;	
+	}
+
+#endif
+	
+	/*
+	 *	Remember if the frame is fragmented.
+	 */
+	 
+	if(iph->frag_off)
+	{
+		if (iph->frag_off & htons(IP_MF))
+			is_frag|=1;
+		/*
+		 *	Last fragment ?
+		 */
+	
+		if (iph->frag_off & htons(IP_OFFSET))
+			is_frag|=2;
+	}
+	
+	/*
+	 *	Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday.
+	 *
+	 *	This is inefficient. While finding out if it is for us we could also compute
+	 *	the routing table entry. This is where the great unified cache theory comes
+	 *	in as and when someone implements it
+	 *
+	 *	For most hosts over 99% of packets match the first conditional
+	 *	and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at
+	 *	function entry.
+	 */
+
+	if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
+	{
+	        if (opt && opt->srr) 
+	        {
+			int srrspace, srrptr;
+			__u32 nexthop;
+			unsigned char * optptr = ((unsigned char *)iph) + opt->srr;
+
+			if (brd != IS_MYADDR || skb->pkt_type != PACKET_HOST) 
+			{
+				kfree_skb(skb, FREE_WRITE);
+				return 0;
+			}
+
+			for ( srrptr=optptr[2], srrspace = optptr[1];
+			      srrptr <= srrspace;
+			      srrptr += 4
+			     ) 
+			{
+				int brd2;
+				if (srrptr + 3 > srrspace) 
+				{
+					icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2,
+						  skb->dev);
+					kfree_skb(skb, FREE_WRITE);
+					return 0;
+				}
+				memcpy(&nexthop, &optptr[srrptr-1], 4);
+				if ((brd2 = ip_chk_addr(nexthop)) == 0)
+					break;
+				if (brd2 != IS_MYADDR) 
+				{
+
+					/*
+					 *	ANK: should we implement weak tunneling of multicasts?
+					 *	Are they obsolete? DVMRP specs (RFC-1075) is old enough...
+					 *	[They are obsolete]
+					 */
+					kfree_skb(skb, FREE_WRITE);
+					return -EINVAL;
+				}
+			}
+			if (srrptr <= srrspace) 
+			{
+				opt->srr_is_hit = 1;
+				opt->is_changed = 1;
+#ifdef CONFIG_IP_FORWARD
+				if (ip_forward(skb, dev, is_frag, nexthop))
+					kfree_skb(skb, FREE_WRITE);
+#else
+				ip_statistics.IpInAddrErrors++;
+				kfree_skb(skb, FREE_WRITE);
+#endif
+				return 0;
+			}
+		}
+
+#ifdef CONFIG_IP_MULTICAST	
+		if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))
+		{
+			/*
+			 *	Check it is for one of our groups
+			 */
+			struct ip_mc_list *ip_mc=dev->ip_mc_list;
+			do
+			{
+				if(ip_mc==NULL)
+				{	
+					kfree_skb(skb, FREE_WRITE);
+					return 0;
+				}
+				if(ip_mc->multiaddr==iph->daddr)
+					break;
+				ip_mc=ip_mc->next;
+			}
+			while(1);
+		}
+#endif
+
+#ifdef CONFIG_IP_MASQUERADE
+		/*
+		 * Do we need to de-masquerade this fragment?
+		 */
+		if (ip_fw_demasquerade(skb)) 
+		{
+			struct iphdr *iph=skb->h.iph;
+			if (ip_forward(skb, dev, is_frag|4, iph->daddr))
+				kfree_skb(skb, FREE_WRITE);
+			return(0);
+		}
+#endif
+
+		/*
+		 *	Account for the packet
+		 */
+ 
+#ifdef CONFIG_IP_ACCT
+		ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
+#endif	
+
+		/*
+		 *	Reassemble IP fragments.
+		 */
+
+		if(is_frag)
+		{
+			/* Defragment. Obtain the complete packet if there is one */
+			skb=ip_defrag(iph,skb,dev);
+			if(skb==NULL)
+				return 0;
+			skb->dev = dev;
+			iph=skb->h.iph;
+		}
+
+		/*
+		 *	Point into the IP datagram, just past the header.
+		 */
+
+		skb->ip_hdr = iph;
+		skb->h.raw += iph->ihl*4;
+
+#ifdef CONFIG_IP_MROUTE		
+		/*
+		 *	Check the state on multicast routing (multicast and not 224.0.0.z)
+		 */
+		 
+		if(brd==IS_MULTICAST && (iph->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000))
+			mroute_pkt=1;
+
+#endif
+		/*
+		 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
+		 *
+		 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
+		 */
+ 
+		hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
+
+		/* 
+		 *	If there maybe a raw socket we must check - if not we don't care less 
+		 */
+		 
+		if((raw_sk=raw_prot.sock_array[hash])!=NULL)
+		{
+			struct sock *sknext=NULL;
+			struct sk_buff *skb1;
+			raw_sk=get_sock_raw(raw_sk, iph->protocol,  iph->saddr, iph->daddr);
+			if(raw_sk)	/* Any raw sockets */
+			{
+				do
+				{
+					/* Find the next */
+					sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
+					if(sknext)
+						skb1=skb_clone(skb, GFP_ATOMIC);
+					else
+						break;	/* One pending raw socket left */
+					if(skb1)
+						raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);
+					raw_sk=sknext;
+				}
+				while(raw_sk!=NULL);
+				
+				/*
+				 *	Here either raw_sk is the last raw socket, or NULL if none 
+				 */
+				 
+				/*
+				 *	We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy 
+				 */
+			}
+		}
+	
+		/*
+		 *	skb->h.raw now points at the protocol beyond the IP header.
+		 */
+	
+		hash = iph->protocol & (MAX_INET_PROTOS -1);
+		for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
+		{
+			struct sk_buff *skb2;
+	
+			if (ipprot->protocol != iph->protocol)
+				continue;
+		       /*
+			* 	See if we need to make a copy of it.  This will
+			* 	only be set if more than one protocol wants it.
+			* 	and then not for the last one. If there is a pending
+			*	raw delivery wait for that
+			*/
+	
+#ifdef CONFIG_IP_MROUTE
+			if (ipprot->copy || raw_sk || mroute_pkt)
+#else	
+			if (ipprot->copy || raw_sk)
+#endif			
+			{
+				skb2 = skb_clone(skb, GFP_ATOMIC);
+				if(skb2==NULL)
+					continue;
+			}
+			else
+			{
+				skb2 = skb;
+			}
+			flag = 1;
+
+		       /*
+			*	Pass on the datagram to each protocol that wants it,
+			*	based on the datagram protocol.  We should really
+			*	check the protocol handler's return values here...
+			*/
+
+			ipprot->handler(skb2, dev, opt, iph->daddr,
+				(ntohs(iph->tot_len) - (iph->ihl * 4)),
+				iph->saddr, 0, ipprot);
+		}
+
+		/*
+		 *	All protocols checked.
+		 *	If this packet was a broadcast, we may *not* reply to it, since that
+		 *	causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+		 *	ICMP reply messages get queued up for transmission...)
+		 */
+
+#ifdef CONFIG_IP_MROUTE		 
+		/*
+		 *	Forward the last copy to the multicast router. If
+		 *	there is a pending raw deliery however make a copy
+		 *	and forward that.
+		 */
+		 
+		if(mroute_pkt)
+		{
+			flag=1;
+			if(raw_sk==NULL)
+				ipmr_forward(skb, is_frag);
+			else
+			{
+				struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
+				if(skb2)
+				{
+					skb2->free=1;
+					ipmr_forward(skb2, is_frag);
+				}
+			}
+		}
+#endif		
+
+		if(raw_sk!=NULL)	/* Shift to last raw user */
+			raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);
+		else if (!flag)		/* Free and report errors */
+		{
+			if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);	
+			kfree_skb(skb, FREE_WRITE);
+		}
+
+		return(0);
+	}
+
+	/*
+	 *	Do any unicast IP forwarding required.
+	 */
+	
+	/*
+	 *	Don't forward multicast or broadcast frames.
+	 */
+
+	if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)
+	{
+		kfree_skb(skb,FREE_WRITE);
+		return 0;
+	}
+
+	/*
+	 *	The packet is for another target. Forward the frame
+	 */
+
+#ifdef CONFIG_IP_FORWARD
+	if (opt && opt->is_strictroute) 
+	{
+	      icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev);
+	      kfree_skb(skb, FREE_WRITE);
+	      return -1;
+	}
+	if (ip_forward(skb, dev, is_frag, iph->daddr))
+		kfree_skb(skb, FREE_WRITE);
+#else
+/*	printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
+			iph->saddr,iph->daddr);*/
+	ip_statistics.IpInAddrErrors++;
+	kfree_skb(skb, FREE_WRITE);
+#endif
+	return(0);
+}
+	
+
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v1.3.35/linux/net/ipv4/ip_options.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/ip_options.c	Tue Oct 17 13:42:37 1995
@@ -0,0 +1,475 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The options processing module for ip.c
+ *
+ * Authors:	A.N.Kuznetsov
+ *		
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+
+/* 
+ * Write options to IP header, record destination address to
+ * source route option, address of outgoing interface
+ * (we should already know it, so that this  function is allowed be
+ * called only after routing decision) and timestamp,
+ * if we originate this datagram.
+ */
+
+void ip_options_build(struct sk_buff * skb, struct options * opt,
+			    __u32 daddr, __u32 saddr,
+			    int is_frag) 
+{
+	unsigned char * iph = (unsigned char*)skb->ip_hdr;
+
+	memcpy(skb->proto_priv, opt, sizeof(struct options));
+	memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
+	opt = (struct options*)skb->proto_priv;
+	opt->is_data = 0;
+
+	if (opt->srr)
+		memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
+
+	if (!is_frag) 
+	{
+		if (opt->rr_needaddr)
+			memcpy(iph+opt->rr+iph[opt->rr+2]-5, &saddr, 4);
+		if (opt->ts_needaddr)
+			memcpy(iph+opt->ts+iph[opt->ts+2]-9, &saddr, 4);
+		if (opt->ts_needtime) 
+		{
+			struct timeval tv;
+			__u32 midtime;
+			do_gettimeofday(&tv);
+			midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+			memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
+		}
+		return;
+	}
+	if (opt->rr) 
+	{
+		memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
+		opt->rr = 0;
+		opt->rr_needaddr = 0;
+	}
+	if (opt->ts) 
+	{
+		memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
+		opt->ts = 0;
+		opt->ts_needaddr = opt->ts_needtime = 0;
+	}
+}
+
+int ip_options_echo(struct options * dopt, struct options * sopt,
+		     __u32 daddr, __u32 saddr,
+		     struct sk_buff * skb) 
+{
+	unsigned char *sptr, *dptr;
+	int soffset, doffset;
+	int	optlen;
+
+	memset(dopt, 0, sizeof(struct options));
+
+	dopt->is_data = 1;
+
+	if (!sopt)
+		sopt = (struct options*)skb->proto_priv;
+
+	if (sopt->optlen == 0) 
+	{
+		dopt->optlen = 0;
+		return 0;
+	}
+
+	sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) :
+		(unsigned char *)skb->ip_hdr);
+	dptr = dopt->__data;
+
+	if (sopt->rr) 
+	{
+		optlen  = sptr[sopt->rr+1];
+		soffset = sptr[sopt->rr+2];
+		dopt->rr = dopt->optlen + sizeof(struct iphdr);
+		memcpy(dptr, sptr+sopt->rr, optlen);
+		if (sopt->rr_needaddr && soffset <= optlen) {
+			if (soffset + 3 > optlen)
+			  return -EINVAL;
+			dptr[2] = soffset + 4;
+			dopt->rr_needaddr = 1;
+		}
+		dptr 	 += optlen;
+		dopt->optlen += optlen;
+	}
+	if (sopt->ts) 
+	{
+		optlen = sptr[sopt->ts+1];
+		soffset = sptr[sopt->ts+2];
+		dopt->ts = dopt->optlen + sizeof(struct iphdr);
+		memcpy(dptr, sptr+sopt->ts, optlen);
+		if (soffset <= optlen) 
+		{
+			if (dopt->ts_needaddr) 
+			{
+				if (soffset + 3 > optlen)
+					return -EINVAL;
+				dopt->ts_needaddr = 1;
+				soffset += 4;
+			}
+			if (dopt->ts_needtime) 
+			{
+				if (soffset + 3 > optlen)
+					return -EINVAL;
+				dopt->ts_needtime = 1;
+				soffset += 4;
+			}
+			if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) 
+			{
+				__u32 addr;
+				memcpy(&addr, sptr+soffset-9, 4);
+				if (ip_chk_addr(addr) == 0) 
+				{
+					dopt->ts_needtime = 0;
+					dopt->ts_needaddr = 0;
+					soffset -= 8;
+				}
+			}
+			dptr[2] = soffset;
+		}
+		dptr += optlen;
+		dopt->optlen += optlen;
+	}
+	if (sopt->srr) 
+	{
+		unsigned char * start = sptr+sopt->srr;
+		__u32 faddr;
+
+		optlen  = start[1];
+		soffset = start[2];
+		doffset = 0;
+		if (soffset > optlen)
+			soffset = optlen + 1;
+		soffset -= 4;
+		if (soffset > 3) 
+		{
+			memcpy(&faddr, &start[soffset-1], 4);
+			for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
+				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
+			/*
+			 * RFC1812 requires to fix illegal source routes.
+			 */
+			if (memcmp(&saddr, &start[soffset+3], 4) == 0)
+				doffset -= 4;
+		}
+		if (doffset > 3) 
+		{
+			memcpy(&start[doffset-1], &daddr, 4);
+			dopt->faddr = faddr;
+			dptr[0] = start[0];
+			dptr[1] = doffset+3;
+			dptr[2] = 4;
+			dptr += doffset+3;
+			dopt->srr = dopt->optlen + sizeof(struct iphdr);
+			dopt->optlen += doffset+3;
+			dopt->is_strictroute = sopt->is_strictroute;
+		}
+	}
+	while (dopt->optlen & 3) 
+	{
+		*dptr++ = IPOPT_END;
+		dopt->optlen++;
+	}
+	return 0;
+}
+
+void ip_options_fragment(struct sk_buff * skb) 
+{
+	unsigned char * optptr = (unsigned char*)skb->ip_hdr;
+	struct options * opt = (struct options*)skb->proto_priv;
+	int  l = opt->optlen;
+	int  optlen;
+
+	while (l > 0) 
+	{
+		switch (*optptr) 
+		{
+		      case IPOPT_END:
+			return;
+		      case IPOPT_NOOP:
+			l--;
+			optptr++;
+			continue;
+		}
+		optlen = optptr[1];
+		if (l<2 || optlen>l)
+		  return;
+		if (!(*optptr & 0x80))
+			memset(optptr, IPOPT_NOOP, optlen);
+		l -= optlen;
+		optptr += optlen;
+	}
+	opt->ts = 0;
+	opt->rr = 0;
+	opt->rr_needaddr = 0;
+	opt->ts_needaddr = 0;
+	opt->ts_needtime = 0;
+	return;
+}
+
+/*
+ * Verify options and fill pointers in struct optinos.
+ * Caller should clear *opt, and set opt->data.
+ * If opt == NULL, then skb->data should point to IP header.
+ */
+
+int ip_options_compile(struct options * opt, struct sk_buff * skb)
+{
+	int l;
+	unsigned char * iph;
+	unsigned char * optptr;
+	int optlen;
+	unsigned char * pp_ptr = NULL;
+
+	if (!opt) 
+	{
+		opt = (struct options*)skb->proto_priv;
+		memset(opt, 0, sizeof(struct options));
+		iph = (unsigned char*)skb->ip_hdr;
+		opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
+		optptr = iph + sizeof(struct iphdr);
+		opt->is_data = 0;
+	}
+	else 
+	{
+		optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1];
+		iph = optptr - sizeof(struct iphdr);
+	}
+
+	for (l = opt->optlen; l > 0; ) 
+	{
+		switch (*optptr) 
+		{
+		      case IPOPT_END:
+			for (optptr++, l--; l>0; l--) 
+			{
+				if (*optptr != IPOPT_END) 
+				{
+					*optptr = IPOPT_END;
+					opt->is_changed = 1;
+				}
+			}
+			goto eol;
+		      case IPOPT_NOOP:
+			l--;
+			optptr++;
+			continue;
+		}
+		optlen = optptr[1];
+		if (l<2 || optlen>l) 
+		{
+			pp_ptr = optptr;
+			break;
+		}
+		switch (*optptr) 
+		{
+		      case IPOPT_SSRR:
+		      case IPOPT_LSRR:
+			if (optlen < 3) 
+			{
+				pp_ptr = optptr + 1;
+				break;
+			}
+			if (optptr[2] < 4) 
+			{
+				pp_ptr = optptr + 2;
+				break;
+			}
+			/* NB: cf RFC-1812 5.2.4.1 */
+			if (opt->srr) 
+			{
+				pp_ptr = optptr;
+				break;
+			}
+			if (!skb) 
+			{
+				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) 
+				{
+					pp_ptr = optptr + 1;
+					break;
+				}
+				memcpy(&opt->faddr, &optptr[3], 4);
+				if (optlen > 7)
+					memmove(&optptr[3], &optptr[7], optlen-7);
+			}
+			opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
+			opt->srr = optptr - iph;
+			break;
+		      case IPOPT_RR:
+			if (opt->rr) 
+			{
+				pp_ptr = optptr;
+				break;
+			}
+			if (optlen < 3) 
+			{
+				pp_ptr = optptr + 1;
+				break;
+			}
+			if (optptr[2] < 4) 
+			{
+				pp_ptr = optptr + 2;
+				break;
+			}
+			if (optptr[2] <= optlen) 
+			{
+				if (optptr[2]+3 > optlen) 
+				{
+					pp_ptr = optptr + 2;
+					break;
+				}
+				if (skb) 
+				{
+					memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4);
+					opt->is_changed = 1;
+				}
+				optptr[2] += 4;
+				opt->rr_needaddr = 1;
+			}
+			opt->rr = optptr - iph;
+			break;
+		      case IPOPT_TIMESTAMP:
+			if (opt->ts) 
+			{
+				pp_ptr = optptr;
+				break;
+			}
+			if (optlen < 4) 
+			{
+				pp_ptr = optptr + 1;
+				break;
+			}
+			if (optptr[2] < 5) 
+			{
+				pp_ptr = optptr + 2;
+				break;
+			}
+			if (optptr[2] <= optlen) 
+			{
+				struct timestamp * ts = (struct timestamp*)(optptr+1);
+				__u32 * timeptr = NULL;
+				if (ts->ptr+3 > ts->len) 
+				{
+					pp_ptr = optptr + 2;
+					break;
+				}
+				switch (ts->flags) 
+				{
+				      case IPOPT_TS_TSONLY:
+					opt->ts = optptr - iph;
+					if (skb) 
+					{
+						timeptr = (__u32*)&optptr[ts->ptr-1];
+						opt->is_changed = 1;
+					}
+					ts->ptr += 4;
+					break;
+				      case IPOPT_TS_TSANDADDR:
+					if (ts->ptr+7 > ts->len) 
+					{
+						pp_ptr = optptr + 2;
+						break;
+					}
+					opt->ts = optptr - iph;
+					if (skb) 
+					{
+						memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4);
+						timeptr = (__u32*)&optptr[ts->ptr+3];
+					}
+					opt->ts_needaddr = 1;
+					opt->ts_needtime = 1;
+					ts->ptr += 8;
+					break;
+				      case IPOPT_TS_PRESPEC:
+					if (ts->ptr+7 > ts->len) 
+					{
+						pp_ptr = optptr + 2;
+						break;
+					}
+					opt->ts = optptr - iph;
+					{
+						__u32 addr;
+						memcpy(&addr, &optptr[ts->ptr-1], 4);
+						if (ip_chk_addr(addr) == 0)
+							break;
+						if (skb)
+							timeptr = (__u32*)&optptr[ts->ptr+3];
+					}
+					opt->ts_needaddr = 1;
+					opt->ts_needtime = 1;
+					ts->ptr += 8;
+					break;
+				      default:
+					pp_ptr = optptr + 3;
+					break;
+				}
+				if (timeptr) 
+				{
+					struct timeval tv;
+					__u32  midtime;
+					do_gettimeofday(&tv);
+					midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+					memcpy(timeptr, &midtime, sizeof(__u32));
+					opt->is_changed = 1;
+				}
+			} 
+			else 
+			{
+				struct timestamp * ts = (struct timestamp*)(optptr+1);
+				if (ts->overflow == 15) 
+				{
+					pp_ptr = optptr + 3;
+					break;
+				}
+				opt->ts = optptr - iph;
+				if (skb) 
+				{
+					ts->overflow++;
+					opt->is_changed = 1;
+				}
+			}
+			break;
+		      case IPOPT_SEC:
+		      case IPOPT_SID:
+		      default:
+			if (!skb) 
+			{
+				pp_ptr = optptr;
+				break;
+			}
+			break;
+		}
+		l -= optlen;
+		optptr += optlen;
+	}
+
+eol:
+	if (!pp_ptr)
+		return 0;
+
+	if (skb) 
+	{
+		icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev);
+		kfree_skb(skb, FREE_READ);
+	}
+	return -EINVAL;
+}
+
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.35/linux/net/ipv4/ip_output.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/ip_output.c	Tue Oct 17 13:42:37 1995
@@ -0,0 +1,1129 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The Internet Protocol (IP) output module.
+ *
+ * Version:	@(#)ip.c	1.0.16b	9/1/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald Becker, <becker@super.org>
+ *		Alan Cox, <Alan.Cox@linux.org>
+ *		Richard Underwood
+ *		Stefan Becker, <stefanb@yello.ping.de>
+ *		Jorge Cwik, <jorge@laser.satlink.net>
+ *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *
+ *	See ip_input.c for original log
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/icmp.h>
+#include <net/raw.h>
+#include <net/checksum.h>
+#include <linux/igmp.h>
+#include <linux/ip_fw.h>
+#include <linux/firewall.h>
+#include <linux/mroute.h>
+#include <net/netlink.h>
+
+/*
+ *	Loop a packet back to the sender.
+ */
+ 
+static void ip_loopback(struct device *old_dev, struct sk_buff *skb)
+{
+	struct device *dev=&loopback_dev;
+	int len=ntohs(skb->ip_hdr->tot_len);
+	struct sk_buff *newskb=dev_alloc_skb(len+dev->hard_header_len+15);
+	
+	if(newskb==NULL)
+		return;
+		
+	newskb->link3=NULL;
+	newskb->sk=NULL;
+	newskb->dev=dev;
+	newskb->saddr=skb->saddr;
+	newskb->daddr=skb->daddr;
+	newskb->raddr=skb->raddr;
+	newskb->free=1;
+	newskb->lock=0;
+	newskb->users=0;
+	newskb->pkt_type=skb->pkt_type;
+	
+	/*
+	 *	Put a MAC header on the packet
+	 */
+	ip_send(newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
+	/*
+	 *	Add the rest of the data space.	
+	 */
+	newskb->ip_hdr=(struct iphdr *)skb_put(newskb, len);
+	memcpy(newskb->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+
+	/*
+	 *	Copy the data
+	 */
+	memcpy(newskb->ip_hdr,skb->ip_hdr,len);
+
+	/* Recurse. The device check against IFF_LOOPBACK will stop infinite recursion */
+		
+	/*printk("Loopback output queued [%lX to %lX].\n", newskb->ip_hdr->saddr,newskb->ip_hdr->daddr);*/
+	ip_queue_xmit(NULL, dev, newskb, 1);
+}
+
+
+
+/*
+ *	Take an skb, and fill in the MAC header.
+ */
+
+int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+{
+	int mac = 0;
+
+	skb->dev = dev;
+	skb->arp = 1;
+	if (dev->hard_header)
+	{
+		/*
+		 *	Build a hardware header. Source address is our mac, destination unknown
+		 *  	(rebuild header will sort this out)
+		 */
+		skb_reserve(skb,(dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
+		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
+		if (mac < 0)
+		{
+			mac = -mac;
+			skb->arp = 0;
+			skb->raddr = daddr;	/* next routing address */
+		}
+	}
+	return mac;
+}
+
+static int ip_send_room(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+{
+	int mac = 0;
+
+	skb->dev = dev;
+	skb->arp = 1;
+	if (dev->hard_header)
+	{
+		skb_reserve(skb,MAX_HEADER);
+		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
+		if (mac < 0)
+		{
+			mac = -mac;
+			skb->arp = 0;
+			skb->raddr = daddr;	/* next routing address */
+		}
+	}
+	return mac;
+}
+
+int ip_id_count = 0;
+
+/*
+ * This routine builds the appropriate hardware/IP headers for
+ * the routine.  It assumes that if *dev != NULL then the
+ * protocol knows what it's doing, otherwise it uses the
+ * routing/ARP tables to select a device struct.
+ */
+int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
+		struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
+{
+	struct rtable *rt;
+	__u32 raddr;
+	int tmp;
+	__u32 src;
+	struct iphdr *iph;
+	__u32 final_daddr = daddr;
+
+	if (opt && opt->srr)
+		daddr = opt->faddr;
+
+	/*
+	 *	See if we need to look up the device.
+	 */
+
+#ifdef CONFIG_IP_MULTICAST	
+	if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name)
+		*dev=dev_get(skb->sk->ip_mc_name);
+#endif
+	if (*dev == NULL)
+	{
+		if(skb->localroute)
+			rt = ip_rt_local(daddr, NULL, &src);
+		else
+			rt = ip_rt_route(daddr, NULL, &src);
+		if (rt == NULL)
+		{
+			ip_statistics.IpOutNoRoutes++;
+			return(-ENETUNREACH);
+		}
+
+		*dev = rt->rt_dev;
+		/*
+		 *	If the frame is from us and going off machine it MUST MUST MUST
+		 *	have the output device ip address and never the loopback
+		 */
+		if (LOOPBACK(saddr) && !LOOPBACK(daddr))
+			saddr = src;/*rt->rt_dev->pa_addr;*/
+		raddr = rt->rt_gateway;
+
+	}
+	else
+	{
+		/*
+		 *	We still need the address of the first hop.
+		 */
+		if(skb->localroute)
+			rt = ip_rt_local(daddr, NULL, &src);
+		else
+			rt = ip_rt_route(daddr, NULL, &src);
+		/*
+		 *	If the frame is from us and going off machine it MUST MUST MUST
+		 *	have the output device ip address and never the loopback
+		 */
+		if (LOOPBACK(saddr) && !LOOPBACK(daddr))
+			saddr = src;/*rt->rt_dev->pa_addr;*/
+
+		raddr = (rt == NULL) ? 0 : rt->rt_gateway;
+	}
+
+	/*
+	 *	No source addr so make it our addr
+	 */
+	if (saddr == 0)
+		saddr = src;
+
+	/*
+	 *	No gateway so aim at the real destination
+	 */
+	if (raddr == 0)
+		raddr = daddr;
+
+	/*
+	 *	Now build the MAC header.
+	 */
+
+	if(type==IPPROTO_TCP)
+		tmp = ip_send_room(skb, raddr, len, *dev, saddr);
+	else
+		tmp = ip_send(skb, raddr, len, *dev, saddr);
+
+	/*
+	 *	Book keeping
+	 */
+
+	skb->dev = *dev;
+	skb->saddr = saddr;
+
+	/*
+	 *	Now build the IP header.
+	 */
+
+	/*
+	 *	If we are using IPPROTO_RAW, then we don't need an IP header, since
+	 *	one is being supplied to us by the user
+	 */
+
+	if(type == IPPROTO_RAW)
+		return (tmp);
+
+	/*
+	 *	Build the IP addresses
+	 */
+	 
+	if (opt)
+		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen);
+	else
+		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr));
+
+	iph->version  = 4;
+	iph->ihl      = 5;
+	iph->tos      = tos;
+	iph->frag_off = 0;
+	iph->ttl      = ttl;
+	iph->daddr    = daddr;
+	iph->saddr    = saddr;
+	iph->protocol = type;
+	skb->ip_hdr   = iph;
+
+	if (!opt || !opt->optlen)
+		return sizeof(struct iphdr) + tmp;
+	if (opt->is_strictroute && rt && rt->rt_gateway) 
+	{
+		ip_statistics.IpOutNoRoutes++;
+		return -ENETUNREACH;
+	}
+	iph->ihl += opt->optlen>>2;
+	ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0);
+	return iph->ihl*4 + tmp;
+}
+
+
+/*
+ *	Generate a checksum for an outgoing IP datagram.
+ */
+
+void ip_send_check(struct iphdr *iph)
+{
+	iph->check = 0;
+	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+}
+
+/*
+ * Queues a packet to be sent, and starts the transmitter
+ * if necessary.  if free = 1 then we free the block after
+ * transmit, otherwise we don't. If free==2 we not only
+ * free the block but also don't assign a new ip seq number.
+ * This routine also needs to put in the total length,
+ * and compute the checksum
+ */
+
+void ip_queue_xmit(struct sock *sk, struct device *dev,
+	      struct sk_buff *skb, int free)
+{
+	struct iphdr *iph;
+/*	unsigned char *ptr;*/
+
+	/* Sanity check */
+	if (dev == NULL)
+	{
+		NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n"));
+		return;
+	}
+
+	IS_SKB(skb);
+
+	/*
+	 *	Do some book-keeping in the packet for later
+	 */
+
+
+	skb->dev = dev;
+	skb->when = jiffies;
+
+	/*
+	 *	Find the IP header and set the length. This is bad
+	 *	but once we get the skb data handling code in the
+	 *	hardware will push its header sensibly and we will
+	 *	set skb->ip_hdr to avoid this mess and the fixed
+	 *	header length problem
+	 */
+
+	iph = skb->ip_hdr;
+	iph->tot_len = ntohs(skb->len-(((unsigned char *)iph)-skb->data));
+
+#ifdef CONFIG_FIREWALL
+	if(call_out_firewall(PF_INET, skb, iph) < FW_ACCEPT)
+		/* just don't send this packet */
+		return;
+#endif	
+
+	/*
+	 *	No reassigning numbers to fragments...
+	 */
+
+	if(free!=2)
+		iph->id      = htons(ip_id_count++);
+	else
+		free=1;
+
+	/* All buffers without an owner socket get freed */
+	if (sk == NULL)
+		free = 1;
+
+	skb->free = free;
+
+	/*
+	 *	Do we need to fragment. Again this is inefficient.
+	 *	We need to somehow lock the original buffer and use
+	 *	bits of it.
+	 */
+
+	if(ntohs(iph->tot_len)> dev->mtu)
+	{
+		ip_fragment(sk,skb,dev,0);
+		IS_SKB(skb);
+		kfree_skb(skb,FREE_WRITE);
+		return;
+	}
+
+	/*
+	 *	Add an IP checksum
+	 */
+
+	ip_send_check(iph);
+
+	/*
+	 *	Print the frame when debugging
+	 */
+
+	/*
+	 *	More debugging. You cannot queue a packet already on a list
+	 *	Spot this and moan loudly.
+	 */
+	if (skb->next != NULL)
+	{
+		NETDEBUG(printk("ip_queue_xmit: next != NULL\n"));
+		skb_unlink(skb);
+	}
+
+	/*
+	 *	If a sender wishes the packet to remain unfreed
+	 *	we add it to his send queue. This arguably belongs
+	 *	in the TCP level since nobody else uses it. BUT
+	 *	remember IPng might change all the rules.
+	 */
+
+	if (!free)
+	{
+		unsigned long flags;
+		/* The socket now has more outstanding blocks */
+
+		sk->packets_out++;
+
+		/* Protect the list for a moment */
+		save_flags(flags);
+		cli();
+
+		if (skb->link3 != NULL)
+		{
+			NETDEBUG(printk("ip.c: link3 != NULL\n"));
+			skb->link3 = NULL;
+		}
+		if (sk->send_head == NULL)
+		{
+			sk->send_tail = skb;
+			sk->send_head = skb;
+		}
+		else
+		{
+			sk->send_tail->link3 = skb;
+			sk->send_tail = skb;
+		}
+		/* skb->link3 is NULL */
+
+		/* Interrupt restore */
+		restore_flags(flags);
+	}
+	else
+		/* Remember who owns the buffer */
+		skb->sk = sk;
+
+	/*
+	 *	If the indicated interface is up and running, send the packet.
+	 */
+	 
+	ip_statistics.IpOutRequests++;
+#ifdef CONFIG_IP_ACCT
+	ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
+#endif	
+	
+#ifdef CONFIG_IP_MULTICAST	
+
+	/*
+	 *	Multicasts are looped back for other local users
+	 */
+	 
+	if (MULTICAST(iph->daddr) && !(dev->flags&IFF_LOOPBACK))
+	{
+		if(sk==NULL || sk->ip_mc_loop)
+		{
+			if(iph->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
+			{
+				ip_loopback(dev,skb);
+			}
+			else
+			{
+				struct ip_mc_list *imc=dev->ip_mc_list;
+				while(imc!=NULL)
+				{
+					if(imc->multiaddr==iph->daddr)
+					{
+						ip_loopback(dev,skb);
+						break;
+					}
+					imc=imc->next;
+				}
+			}
+		}
+		/* Multicasts with ttl 0 must not go beyond the host */
+		
+		if(skb->ip_hdr->ttl==0)
+		{
+			kfree_skb(skb, FREE_READ);
+			return;
+		}
+	}
+#endif
+	if((dev->flags&IFF_BROADCAST) && (iph->daddr==dev->pa_brdaddr||iph->daddr==0xFFFFFFFF) && !(dev->flags&IFF_LOOPBACK))
+		ip_loopback(dev,skb);
+		
+	if (dev->flags & IFF_UP)
+	{
+		/*
+		 *	If we have an owner use its priority setting,
+		 *	otherwise use NORMAL
+		 */
+
+		if (sk != NULL)
+		{
+			dev_queue_xmit(skb, dev, sk->priority);
+		}
+		else
+		{
+			dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+		}
+	}
+	else
+	{
+		if(sk)
+			sk->err = ENETDOWN;
+		ip_statistics.IpOutDiscards++;
+		if (free)
+			kfree_skb(skb, FREE_WRITE);
+	}
+}
+
+
+/*
+ *	Build and send a packet, with as little as one copy
+ *
+ *	Doesn't care much about ip options... option length can be
+ *	different for fragment at 0 and other fragments.
+ *
+ *	Note that the fragment at the highest offset is sent first,
+ *	so the getfrag routine can fill in the TCP/UDP checksum header
+ *	field in the last fragment it sends... actually it also helps
+ * 	the reassemblers, they can put most packets in at the head of
+ *	the fragment queue, and they know the total size in advance. This
+ *	last feature will measurable improve the Linux fragment handler.
+ *
+ *	The callback has five args, an arbitrary pointer (copy of frag),
+ *	the source IP address (may depend on the routing table), the 
+ *	destination adddress (char *), the offset to copy from, and the
+ *	length to be copied.
+ * 
+ */
+
+int ip_build_xmit(struct sock *sk,
+		   void getfrag (const void *,
+				 __u32,
+				 char *,
+				 unsigned int,	
+				 unsigned int),
+		   const void *frag,
+		   unsigned short int length,
+		   __u32 daddr,
+		   __u32 user_saddr,
+		   struct options * opt,
+		   int flags,
+		   int type) 
+{
+	struct rtable *rt;
+	unsigned int fraglen, maxfraglen, fragheaderlen;
+	int offset, mf;
+	__u32 saddr;
+	unsigned short id;
+	struct iphdr *iph;
+	int local=0;
+	struct device *dev;
+	int nfrags=0;
+	__u32 true_daddr = daddr;
+
+	if (opt && opt->srr && !sk->ip_hdrincl)
+	  daddr = opt->faddr;
+	
+	ip_statistics.IpOutRequests++;
+
+#ifdef CONFIG_IP_MULTICAST	
+	if(sk && MULTICAST(daddr) && *sk->ip_mc_name)
+	{
+		dev=dev_get(sk->ip_mc_name);
+		if(!dev)
+			return -ENODEV;
+		rt=NULL;
+		if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
+			saddr = sk->saddr;
+		else
+			saddr = dev->pa_addr;
+	}
+	else
+	{
+#endif	
+		/*
+		 *	Perform the IP routing decisions
+		 */
+	 
+		if(sk->localroute || flags&MSG_DONTROUTE)
+			local=1;
+	
+		rt = sk->ip_route_cache;
+		
+		/*
+		 *	See if the routing cache is outdated. We need to clean this up once we are happy it is reliable
+		 *	by doing the invalidation actively in the route change and header change.
+		 */
+	
+		saddr=sk->ip_route_saddr;	 
+		if(!rt || sk->ip_route_stamp != rt_stamp ||
+		   daddr!=sk->ip_route_daddr || sk->ip_route_local!=local ||
+		   (sk->saddr && sk->saddr != saddr))
+		{
+			if(local)
+				rt = ip_rt_local(daddr, NULL, &saddr);
+			else
+				rt = ip_rt_route(daddr, NULL, &saddr);
+			sk->ip_route_local=local;
+			sk->ip_route_daddr=daddr;
+			sk->ip_route_saddr=saddr;
+			sk->ip_route_stamp=rt_stamp;
+			sk->ip_route_cache=rt;
+			sk->ip_hcache_ver=NULL;
+			sk->ip_hcache_state= 0;
+		}
+		else if(rt)
+		{
+			/*
+			 *	Attempt header caches only if the cached route is being reused. Header cache
+			 *	is not ultra cheap to set up. This means we only set it up on the second packet,
+			 *	so one shot communications are not slowed. We assume (seems reasonable) that 2 is
+			 *	probably going to be a stream of data.
+			 */
+			if(rt->rt_dev->header_cache && sk->ip_hcache_state!= -1)
+			{
+				if(sk->ip_hcache_ver==NULL || sk->ip_hcache_stamp!=*sk->ip_hcache_ver)
+					rt->rt_dev->header_cache(rt->rt_dev,sk,saddr,daddr);
+				else
+					/* Can't cache. Remember this */
+					sk->ip_hcache_state= -1;
+			}
+		}
+		
+		if (rt == NULL) 
+		{
+	 		ip_statistics.IpOutNoRoutes++;
+			return(-ENETUNREACH);
+		}
+	
+		if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
+			saddr = sk->saddr;
+			
+		dev=rt->rt_dev;
+#ifdef CONFIG_IP_MULTICAST
+	}
+#endif		
+	if (user_saddr)
+		saddr = user_saddr;
+
+	/*
+	 *	Now compute the buffer space we require
+	 */ 
+	 
+	/*
+	 *	Try the simple case first. This leaves broadcast, multicast, fragmented frames, and by
+	 *	choice RAW frames within 20 bytes of maximum size(rare) to the long path
+	 */
+
+	length += 20;
+	if (!sk->ip_hdrincl && opt) 
+	{
+		length += opt->optlen;
+		if (opt->is_strictroute && rt && rt->rt_gateway) 
+		{
+			ip_statistics.IpOutNoRoutes++;
+			return -ENETUNREACH;
+		}
+	}
+	if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr)
+	{	
+		int error;
+		struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+dev->hard_header_len,0, 0,&error);
+		if(skb==NULL)
+		{
+			ip_statistics.IpOutDiscards++;
+			return error;
+		}
+		skb->dev=dev;
+		skb->free=1;
+		skb->when=jiffies;
+		skb->sk=sk;
+		skb->arp=0;
+		skb->saddr=saddr;
+		skb->raddr=(rt&&rt->rt_gateway)?rt->rt_gateway:daddr;
+		skb_reserve(skb,(dev->hard_header_len+15)&~15);
+		if(sk->ip_hcache_state>0)
+		{
+			memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data,dev->hard_header_len);
+			skb->arp=1;
+		}
+		else if(dev->hard_header)
+		{
+			if(dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,0)>0)
+				skb->arp=1;
+		}
+		else
+			skb->arp=1;
+		skb->ip_hdr=iph=(struct iphdr *)skb_put(skb,length);
+		dev_lock_list();
+		if(!sk->ip_hdrincl)
+		{
+			iph->version=4;
+			iph->ihl=5;
+			iph->tos=sk->ip_tos;
+			iph->tot_len = htons(length);
+			iph->id=htons(ip_id_count++);
+			iph->frag_off = 0;
+			iph->ttl=sk->ip_ttl;
+			iph->protocol=type;
+			iph->saddr=saddr;
+			iph->daddr=daddr;
+			if (opt) 
+			{
+				iph->ihl += opt->optlen>>2;
+				ip_options_build(skb, opt,
+						 true_daddr, dev->pa_addr, 0);
+			}
+			iph->check=0;
+			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+			getfrag(frag,saddr,((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
+		}
+		else
+			getfrag(frag,saddr,(void *)iph,0,length-20);
+		dev_unlock_list();
+#ifdef CONFIG_FIREWALL
+		if(call_out_firewall(PF_INET, skb, iph)< FW_ACCEPT)
+		{
+			kfree_skb(skb, FREE_WRITE);
+			return -EPERM;
+		}
+#endif
+#ifdef CONFIG_IP_ACCT
+		ip_fw_chk((void *)skb->data,dev,ip_acct_chain, IP_FW_F_ACCEPT,1);
+#endif		
+		if(dev->flags&IFF_UP)
+			dev_queue_xmit(skb,dev,sk->priority);
+		else
+		{
+			ip_statistics.IpOutDiscards++;
+			kfree_skb(skb, FREE_WRITE);
+		}
+		return 0;
+	}
+	length-=20;
+	if (sk && !sk->ip_hdrincl && opt) 
+	{
+		length -= opt->optlen;
+		fragheaderlen = dev->hard_header_len + sizeof(struct iphdr) + opt->optlen;
+		maxfraglen = ((dev->mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
+	}
+	else 
+	{
+		fragheaderlen = dev->hard_header_len;
+		if(!sk->ip_hdrincl)
+			fragheaderlen += 20;
+		
+		/*
+		 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work
+		 *	out the size of the frames to send.
+		 */
+	 
+		maxfraglen = ((dev->mtu-20) & ~7) + fragheaderlen;
+        }
+	
+	/*
+	 *	Start at the end of the frame by handling the remainder.
+	 */
+	 
+	offset = length - (length % (maxfraglen - fragheaderlen));
+	
+	/*
+	 *	Amount of memory to allocate for final fragment.
+	 */
+	 
+	fraglen = length - offset + fragheaderlen;
+	
+	if(length-offset==0)
+	{
+		fraglen = maxfraglen;
+		offset -= maxfraglen-fragheaderlen;
+	}
+	
+	
+	/*
+	 *	The last fragment will not have MF (more fragments) set.
+	 */
+	 
+	mf = 0;
+
+	/*
+	 *	Can't fragment raw packets 
+	 */
+	 
+	if (sk->ip_hdrincl && offset > 0)
+ 		return(-EMSGSIZE);
+
+	/*
+	 *	Lock the device lists.
+	 */
+
+	dev_lock_list();
+	
+	/*
+	 *	Get an identifier
+	 */
+	 
+	id = htons(ip_id_count++);
+
+	/*
+	 *	Being outputting the bytes.
+	 */
+	 
+	do 
+	{
+		struct sk_buff * skb;
+		int error;
+		char *data;
+
+		/*
+		 *	Get the memory we require with some space left for alignment.
+		 */
+
+		skb = sock_alloc_send_skb(sk, fraglen+15, 0, 0, &error);
+		if (skb == NULL)
+		{
+			ip_statistics.IpOutDiscards++;
+			if(nfrags>1)
+				ip_statistics.IpFragCreates++;			
+			dev_unlock_list();
+			return(error);
+		}
+		
+		/*
+		 *	Fill in the control structures
+		 */
+		 
+		skb->next = skb->prev = NULL;
+		skb->dev = dev;
+		skb->when = jiffies;
+		skb->free = 1; /* dubious, this one */
+		skb->sk = sk;
+		skb->arp = 0;
+		skb->saddr = saddr;
+		skb->raddr = (rt&&rt->rt_gateway) ? rt->rt_gateway : daddr;
+		skb_reserve(skb,(dev->hard_header_len+15)&~15);
+		data = skb_put(skb, fraglen-dev->hard_header_len);
+
+		/*
+		 *	Save us ARP and stuff. In the optimal case we do no route lookup (route cache ok)
+		 *	no ARP lookup (arp cache ok) and output. The cache checks are still too slow but
+		 *	this can be fixed later. For gateway routes we ought to have a rt->.. header cache
+		 *	pointer to speed header cache builds for identical targets.
+		 */
+		 
+		if(sk->ip_hcache_state>0)
+		{
+			memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data, dev->hard_header_len);
+			skb->arp=1;
+		}
+		else if (dev->hard_header)
+		{
+			if(dev->hard_header(skb, dev, ETH_P_IP, 
+						NULL, NULL, 0)>0)
+				skb->arp=1;
+		}
+		
+		/*
+		 *	Find where to start putting bytes.
+		 */
+		 
+		skb->ip_hdr = iph = (struct iphdr *)data;
+
+		/*
+		 *	Only write IP header onto non-raw packets 
+		 */
+		 
+		if(!sk->ip_hdrincl) 
+		{
+
+			iph->version = 4;
+			iph->ihl = 5; /* ugh */
+			if (opt) {
+				iph->ihl += opt->optlen>>2;
+				ip_options_build(skb, opt,
+						 true_daddr, dev->pa_addr, offset);
+			}
+			iph->tos = sk->ip_tos;
+			iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
+			iph->id = id;
+			iph->frag_off = htons(offset>>3);
+			iph->frag_off |= mf;
+#ifdef CONFIG_IP_MULTICAST
+			if (MULTICAST(daddr))
+				iph->ttl = sk->ip_mc_ttl;
+			else
+#endif
+				iph->ttl = sk->ip_ttl;
+			iph->protocol = type;
+			iph->check = 0;
+			iph->saddr = saddr;
+			iph->daddr = daddr;
+			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+			data += iph->ihl*4;
+			
+			/*
+			 *	Any further fragments will have MF set.
+			 */
+			 
+			mf = htons(IP_MF);
+		}
+		
+		/*
+		 *	User data callback
+		 */
+
+		getfrag(frag, saddr, data, offset, fraglen-fragheaderlen);
+		
+		/*
+		 *	Account for the fragment.
+		 */
+		 
+#ifdef CONFIG_FIREWALL
+		if(!offset && call_out_firewall(PF_INET, skb, iph) < FW_ACCEPT)
+		{
+			kfree_skb(skb, FREE_WRITE);
+			dev_unlock_list();
+			return -EPERM;
+		}
+#endif		
+#ifdef CONFIG_IP_ACCT
+		if(!offset)
+			ip_fw_chk(iph, dev, ip_acct_chain, IP_FW_F_ACCEPT, 1);
+#endif	
+		offset -= (maxfraglen-fragheaderlen);
+		fraglen = maxfraglen;
+
+#ifdef CONFIG_IP_MULTICAST
+
+		/*
+		 *	Multicasts are looped back for other local users
+		 */
+	 
+		if (MULTICAST(daddr) && !(dev->flags&IFF_LOOPBACK)) 
+		{
+			/*
+			 *	Loop back any frames. The check for IGMP_ALL_HOSTS is because
+			 *	you are always magically a member of this group.
+			 *
+			 *	Always loop back all host messages when running as a multicast router.
+			 */
+			 
+			if(sk==NULL || sk->ip_mc_loop)
+			{
+				if(skb->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
+					ip_loopback(rt?rt->rt_dev:dev,skb);
+				else 
+				{
+					struct ip_mc_list *imc=rt?rt->rt_dev->ip_mc_list:dev->ip_mc_list;
+					while(imc!=NULL) 
+					{
+						if(imc->multiaddr==daddr) 
+						{
+							ip_loopback(rt?rt->rt_dev:dev,skb);
+							break;
+						}
+						imc=imc->next;
+					}
+				}
+			}
+
+			/*
+			 *	Multicasts with ttl 0 must not go beyond the host. Fixme: avoid the
+			 *	extra clone.
+			 */
+
+			if(skb->ip_hdr->ttl==0)
+				kfree_skb(skb, FREE_READ);
+		}
+#endif
+
+		nfrags++;
+		
+		/*
+		 *	BSD loops broadcasts
+		 */
+		 
+		if((dev->flags&IFF_BROADCAST) && (daddr==0xFFFFFFFF || daddr==dev->pa_brdaddr) && !(dev->flags&IFF_LOOPBACK))
+			ip_loopback(dev,skb);
+
+		/*
+		 *	Now queue the bytes into the device.
+		 */
+		 
+		if (dev->flags & IFF_UP) 
+		{
+			dev_queue_xmit(skb, dev, sk->priority);
+		} 
+		else 
+		{
+			/*
+			 *	Whoops... 
+			 */
+			 
+			ip_statistics.IpOutDiscards++;
+			if(nfrags>1)
+				ip_statistics.IpFragCreates+=nfrags;
+			kfree_skb(skb, FREE_WRITE);
+			dev_unlock_list();
+			/*
+			 *	BSD behaviour.
+			 */
+			if(sk!=NULL)
+				sk->err=ENETDOWN;
+			return(0); /* lose rest of fragments */
+		}
+	} 
+	while (offset >= 0);
+	if(nfrags>1)
+		ip_statistics.IpFragCreates+=nfrags;
+	dev_unlock_list();
+	return(0);
+}
+    
+
+/*
+ *	IP protocol layer initialiser
+ */
+
+static struct packet_type ip_packet_type =
+{
+	0,	/* MUTTER ntohs(ETH_P_IP),*/
+	NULL,	/* All devices */
+	ip_rcv,
+	NULL,
+	NULL,
+};
+
+#ifdef CONFIG_RTNETLINK
+
+/*
+ *	Netlink hooks for IP
+ */
+ 
+void ip_netlink_msg(unsigned long msg, __u32 daddr, __u32 gw, __u32 mask, short flags, short metric, char *name)
+{
+	struct sk_buff *skb=alloc_skb(sizeof(struct netlink_rtinfo), GFP_ATOMIC);
+	struct netlink_rtinfo *nrt;
+	struct sockaddr_in *s;
+	if(skb==NULL)
+		return;
+	nrt=(struct netlink_rtinfo *)skb_put(skb, sizeof(struct netlink_rtinfo));
+	nrt->rtmsg_type=msg;
+	s=(struct sockaddr_in *)&nrt->rtmsg_dst;
+	s->sin_family=AF_INET;
+	s->sin_addr.s_addr=daddr;
+	s=(struct sockaddr_in *)&nrt->rtmsg_gateway;
+	s->sin_family=AF_INET;
+	s->sin_addr.s_addr=gw;
+	s=(struct sockaddr_in *)&nrt->rtmsg_genmask;
+	s->sin_family=AF_INET;
+	s->sin_addr.s_addr=mask;
+	nrt->rtmsg_flags=flags;
+	nrt->rtmsg_metric=metric;
+	strcpy(nrt->rtmsg_device,name);
+	netlink_post(NETLINK_ROUTE, skb);
+}	
+
+#endif
+
+/*
+ *	Device notifier
+ */
+ 
+static int ip_rt_event(unsigned long event, void *ptr)
+{
+	struct device *dev=ptr;
+	if(event==NETDEV_DOWN)
+	{
+		ip_netlink_msg(RTMSG_DELDEVICE, 0,0,0,0,0,dev->name);
+		ip_rt_flush(dev);
+	}
+/*
+ *	Join the intial group if multicast.
+ */		
+	if(event==NETDEV_UP)
+	{
+#ifdef CONFIG_IP_MULTICAST	
+		ip_mc_allhost(dev);
+#endif		
+		ip_netlink_msg(RTMSG_NEWDEVICE, 0,0,0,0,0,dev->name);
+	}
+	return NOTIFY_DONE;
+}
+
+struct notifier_block ip_rt_notifier={
+	ip_rt_event,
+	NULL,
+	0
+};
+
+/*
+ *	IP registers the packet type and then calls the subprotocol initialisers
+ */
+
+void ip_init(void)
+{
+	ip_packet_type.type=htons(ETH_P_IP);
+	dev_add_pack(&ip_packet_type);
+
+	/* So we flush routes when a device is downed */	
+	register_netdevice_notifier(&ip_rt_notifier);
+
+/*	ip_raw_init();
+	ip_packet_init();
+	ip_tcp_init();
+	ip_udp_init();*/
+
+#ifdef CONFIG_IP_MULTICAST
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_IGMP, 4, "igmp",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		ip_mc_procinfo
+	});
+#endif
+}
+
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v1.3.35/linux/net/ipv4/ip_sockglue.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/ip_sockglue.c	Fri Oct 20 13:33:46 1995
@@ -0,0 +1,529 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The IP to API glue.
+ *		
+ * Authors:	see ip.c
+ *
+ * Fixes:
+ *		Many		:	Split from ip.c , see ip.c for history.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#include <net/checksum.h>
+#include <linux/route.h>
+#include <linux/mroute.h>
+#include <net/route.h>
+
+#include <asm/segment.h>
+
+#ifdef CONFIG_IP_MULTICAST
+
+/*
+ *	Write an multicast group list table for the IGMP daemon to
+ *	read.
+ */
+ 
+int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	off_t pos=0, begin=0;
+	struct ip_mc_list *im;
+	unsigned long flags;
+	int len=0;
+	struct device *dev;
+	
+	len=sprintf(buffer,"Device    : Count\tGroup    Users Timer\n");  
+	save_flags(flags);
+	cli();
+	
+	for(dev = dev_base; dev; dev = dev->next)
+	{
+                if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST))
+                {
+                        len+=sprintf(buffer+len,"%-10s: %5d\n",
+					dev->name, dev->mc_count);
+                        for(im = dev->ip_mc_list; im; im = im->next)
+                        {
+                                len+=sprintf(buffer+len,
+					"\t\t\t%08lX %5d %d:%08lX\n",
+                                        im->multiaddr, im->users,
+					im->tm_running, im->timer.expires-jiffies);
+                                pos=begin+len;
+                                if(pos<offset)
+                                {
+                                        len=0;
+                                        begin=pos;
+                                }
+                                if(pos>offset+length)
+                                        break;
+                        }
+                }
+	}
+	restore_flags(flags);
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;	
+	return len;
+}
+
+
+/*
+ *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
+ *	an IP socket.
+ *
+ *	We implement IP_TOS (type of service), IP_TTL (time to live).
+ *
+ *	Next release we will sort out IP_OPTIONS since for some people are kind of important.
+ */
+
+static struct device *ip_mc_find_devfor(unsigned long addr)
+{
+	struct device *dev;
+	for(dev = dev_base; dev; dev = dev->next)
+	{
+		if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
+			(dev->pa_addr==addr))
+			return dev;
+	}
+
+	return NULL;
+}
+
+#endif
+
+int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
+{
+	int val,err;
+	unsigned char ucval;
+#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
+	struct ip_fw tmp_fw;
+#endif	
+	if (optval == NULL)
+	{
+		val=0;
+		ucval=0;
+	}
+	else
+	{
+		err=verify_area(VERIFY_READ, optval, sizeof(int));
+		if(err)
+			return err;
+		val = get_user((int *) optval);
+		ucval=get_user((unsigned char *) optval);
+	}
+	
+	if(level!=SOL_IP)
+		return -EOPNOTSUPP;
+#ifdef CONFIG_IP_MROUTE
+	if(optname>=MRT_BASE && optname <=MRT_BASE+10)
+	{
+		return ip_mroute_setsockopt(sk,optname,optval,optlen);
+	}
+#endif
+	
+	switch(optname)
+	{
+		case IP_OPTIONS:
+	          {
+			  struct options * opt = NULL;
+			  struct options * old_opt;
+			  if (optlen > 40 || optlen < 0)
+			  	return -EINVAL;
+			  err = verify_area(VERIFY_READ, optval, optlen);
+			  if (err)
+			  	return err;
+			  opt = kmalloc(sizeof(struct options)+((optlen+3)&~3), GFP_KERNEL);
+			  if (!opt)
+			  	return -ENOMEM;
+			  memset(opt, 0, sizeof(struct options));
+			  if (optlen)
+			  	memcpy_fromfs(opt->__data, optval, optlen);
+			  while (optlen & 3)
+			  	opt->__data[optlen++] = IPOPT_END;
+			  opt->optlen = optlen;
+			  opt->is_data = 1;
+			  opt->is_setbyuser = 1;
+			  if (optlen && ip_options_compile(opt, NULL)) 
+			  {
+				  kfree_s(opt, sizeof(struct options) + optlen);
+				  return -EINVAL;
+			  }
+			  /*
+			   * ANK: I'm afraid that receive handler may change
+			   * options from under us.
+			   */
+			  cli();
+			  old_opt = sk->opt;
+			  sk->opt = opt;
+			  sti();
+			  if (old_opt)
+			  	kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
+			  return 0;
+		  }
+		case IP_TOS:
+			if(val<0||val>255)
+				return -EINVAL;
+			sk->ip_tos=val;
+			if(val==IPTOS_LOWDELAY)
+				sk->priority=SOPRI_INTERACTIVE;
+			if(val==IPTOS_THROUGHPUT)
+				sk->priority=SOPRI_BACKGROUND;
+			return 0;
+		case IP_TTL:
+			if(val<1||val>255)
+				return -EINVAL;
+			sk->ip_ttl=val;
+			return 0;
+		case IP_HDRINCL:
+			if(sk->type!=SOCK_RAW)
+				return -ENOPROTOOPT;
+			sk->ip_hdrincl=val?1:0;
+			return 0;
+#ifdef CONFIG_IP_MULTICAST
+		case IP_MULTICAST_TTL: 
+		{
+			sk->ip_mc_ttl=(int)ucval;
+	                return 0;
+		}
+		case IP_MULTICAST_LOOP: 
+		{
+			if(ucval!=0 && ucval!=1)
+				 return -EINVAL;
+			sk->ip_mc_loop=(int)ucval;
+			return 0;
+		}
+		case IP_MULTICAST_IF: 
+		{
+			struct in_addr addr;
+			struct device *dev=NULL;
+			
+			/*
+			 *	Check the arguments are allowable
+			 */
+
+			err=verify_area(VERIFY_READ, optval, sizeof(addr));
+			if(err)
+				return err;
+				
+			memcpy_fromfs(&addr,optval,sizeof(addr));
+			
+			
+			/*
+			 *	What address has been requested
+			 */
+			
+			if(addr.s_addr==INADDR_ANY)	/* Default */
+			{
+				sk->ip_mc_name[0]=0;
+				return 0;
+			}
+			
+			/*
+			 *	Find the device
+			 */
+			 
+			dev=ip_mc_find_devfor(addr.s_addr);
+						
+			/*
+			 *	Did we find one
+			 */
+			 
+			if(dev) 
+			{
+				strcpy(sk->ip_mc_name,dev->name);
+				return 0;
+			}
+			return -EADDRNOTAVAIL;
+		}
+		
+		case IP_ADD_MEMBERSHIP: 
+		{
+		
+/*
+ *	FIXME: Add/Del membership should have a semaphore protecting them from re-entry
+ */
+			struct ip_mreq mreq;
+			__u32 route_src;
+			struct rtable *rt;
+			struct device *dev=NULL;
+			
+			/*
+			 *	Check the arguments.
+			 */
+
+			err=verify_area(VERIFY_READ, optval, sizeof(mreq));
+			if(err)
+				return err;
+
+			memcpy_fromfs(&mreq,optval,sizeof(mreq));
+
+			/* 
+			 *	Get device for use later
+			 */
+
+			if(mreq.imr_interface.s_addr==INADDR_ANY) 
+			{
+				/*
+				 *	Not set so scan.
+				 */
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
+				{
+					dev=rt->rt_dev;
+					rt->rt_use--;
+				}
+			}
+			else
+			{
+				/*
+				 *	Find a suitable device.
+				 */
+				
+				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
+			}
+			
+			/*
+			 *	No device, no cookies.
+			 */
+			 
+			if(!dev)
+				return -ENODEV;
+				
+			/*
+			 *	Join group.
+			 */
+			 
+			return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
+		
+		case IP_DROP_MEMBERSHIP: 
+		{
+			struct ip_mreq mreq;
+			struct rtable *rt;
+			__u32 route_src;
+			struct device *dev=NULL;
+
+			/*
+			 *	Check the arguments
+			 */
+			 
+			err=verify_area(VERIFY_READ, optval, sizeof(mreq));
+			if(err)
+				return err;
+
+			memcpy_fromfs(&mreq,optval,sizeof(mreq));
+
+			/*
+			 *	Get device for use later 
+			 */
+ 
+			if(mreq.imr_interface.s_addr==INADDR_ANY) 
+			{
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
+			        {
+					dev=rt->rt_dev;
+					rt->rt_use--;
+				}
+			}
+			else 
+			{
+			
+				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
+			}
+			
+			/*
+			 *	Did we find a suitable device.
+			 */
+			 
+			if(!dev)
+				return -ENODEV;
+				
+			/*
+			 *	Leave group
+			 */
+			 
+			return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
+#endif			
+#ifdef CONFIG_IP_FIREWALL
+		case IP_FW_ADD_BLK:
+		case IP_FW_DEL_BLK:
+		case IP_FW_ADD_FWD:
+		case IP_FW_DEL_FWD:
+		case IP_FW_CHK_BLK:
+		case IP_FW_CHK_FWD:
+		case IP_FW_FLUSH_BLK:
+		case IP_FW_FLUSH_FWD:
+		case IP_FW_ZERO_BLK:
+		case IP_FW_ZERO_FWD:
+		case IP_FW_POLICY_BLK:
+		case IP_FW_POLICY_FWD:
+			if(!suser())
+				return -EPERM;
+			if(optlen>sizeof(tmp_fw) || optlen<1)
+				return -EINVAL;
+			err=verify_area(VERIFY_READ,optval,optlen);
+			if(err)
+				return err;
+			memcpy_fromfs(&tmp_fw,optval,optlen);
+			err=ip_fw_ctl(optname, &tmp_fw,optlen);
+			return -err;	/* -0 is 0 after all */
+			
+#endif
+#ifdef CONFIG_IP_ACCT
+		case IP_ACCT_DEL:
+		case IP_ACCT_ADD:
+		case IP_ACCT_FLUSH:
+		case IP_ACCT_ZERO:
+			if(!suser())
+				return -EPERM;
+			if(optlen>sizeof(tmp_fw) || optlen<1)
+				return -EINVAL;
+			err=verify_area(VERIFY_READ,optval,optlen);
+			if(err)
+				return err;
+			memcpy_fromfs(&tmp_fw, optval,optlen);
+			err=ip_acct_ctl(optname, &tmp_fw,optlen);
+			return -err;	/* -0 is 0 after all */
+#endif
+		/* IP_OPTIONS and friends go here eventually */
+		default:
+			return(-ENOPROTOOPT);
+	}
+}
+
+/*
+ *	Get the options. Note for future reference. The GET of IP options gets the
+ *	_received_ ones. The set sets the _sent_ ones.
+ */
+
+int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
+{
+	int val,err;
+#ifdef CONFIG_IP_MULTICAST
+	int len;
+#endif
+	
+	if(level!=SOL_IP)
+		return -EOPNOTSUPP;
+
+#ifdef CONFIG_IP_MROUTE
+	if(optname>=MRT_BASE && optname <=MRT_BASE+10)
+	{
+		return ip_mroute_getsockopt(sk,optname,optval,optlen);
+	}
+#endif
+
+	switch(optname)
+	{
+		case IP_OPTIONS:
+			{
+				unsigned char optbuf[sizeof(struct options)+40];
+				struct options * opt = (struct options*)optbuf;
+				err = verify_area(VERIFY_WRITE, optlen, sizeof(int));
+				if (err)
+					return err;
+				cli();
+				opt->optlen = 0;
+				if (sk->opt)
+					memcpy(optbuf, sk->opt, sizeof(struct options)+sk->opt->optlen);
+				sti();
+				if (opt->optlen == 0) 
+				{
+					put_fs_long(0,(unsigned long *) optlen);
+					return 0;
+				}
+				err = verify_area(VERIFY_WRITE, optval, opt->optlen);
+				if (err)
+					return err;
+/*
+ * Now we should undo all the changes done by ip_options_compile().
+ */
+				if (opt->srr) 
+				{
+					unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
+					memmove(optptr+7, optptr+4, optptr[1]-7);
+					memcpy(optptr+3, &opt->faddr, 4);
+				}
+				if (opt->rr_needaddr) 
+				{
+					unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
+					memset(&optptr[optptr[2]-1], 0, 4);
+					optptr[2] -= 4;
+				}
+				if (opt->ts) 
+				{
+					unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
+					if (opt->ts_needtime) 
+					{
+						memset(&optptr[optptr[2]-1], 0, 4);
+						optptr[2] -= 4;
+					}
+					if (opt->ts_needaddr) 
+					{
+						memset(&optptr[optptr[2]-1], 0, 4);
+						optptr[2] -= 4;
+					}
+				}
+				put_fs_long(opt->optlen, (unsigned long *) optlen);
+				memcpy_tofs(optval, opt->__data, opt->optlen);
+			}
+			return 0;
+		case IP_TOS:
+			val=sk->ip_tos;
+			break;
+		case IP_TTL:
+			val=sk->ip_ttl;
+			break;
+		case IP_HDRINCL:
+			val=sk->ip_hdrincl;
+			break;
+#ifdef CONFIG_IP_MULTICAST			
+		case IP_MULTICAST_TTL:
+			val=sk->ip_mc_ttl;
+			break;
+		case IP_MULTICAST_LOOP:
+			val=sk->ip_mc_loop;
+			break;
+		case IP_MULTICAST_IF:
+			err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
+			if(err)
+  				return err;
+  			len=strlen(sk->ip_mc_name);
+  			err=verify_area(VERIFY_WRITE, optval, len);
+		  	if(err)
+  				return err;
+  			put_user(len,(int *) optlen);
+			memcpy_tofs((void *)optval,sk->ip_mc_name, len);
+			return 0;
+#endif
+		default:
+			return(-ENOPROTOOPT);
+	}
+	err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
+	if(err)
+		return err;
+	put_user(sizeof(int),(int *) optlen);
+
+	err=verify_area(VERIFY_WRITE, optval, sizeof(int));
+	if(err)
+		return err;
+	put_user(val,(int *) optval);
+
+	return(0);
+}
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v1.3.35/linux/net/ipv4/ipip.c	Wed Sep 27 16:00:02 1995
+++ linux/net/ipv4/ipip.c	Tue Oct 17 13:42:37 1995
@@ -32,7 +32,7 @@
 #include <linux/udp.h>
 #include <net/protocol.h>
 #include <net/ipip.h>
-#include <linux/ip_fw.h>
+#include <linux/firewall.h>
 
 /*
  * NB. we must include the kernel idenfication string in to install the module.
@@ -84,17 +84,18 @@
 	skb->h.iph=(struct iphdr *)skb->data;
 	skb->ip_hdr=(struct iphdr *)skb->data;
 	memset(skb->proto_priv, 0, sizeof(struct options));
-	if (skb->ip_hdr->ihl > 5) {
-	  if (ip_options_compile(NULL, skb))
-	    return 0;
+	if (skb->ip_hdr->ihl > 5) 
+	{
+		if (ip_options_compile(NULL, skb))
+			return 0;
 	}
 	
-#ifdef CONFIG_IP_FIREWALL
+#ifdef CONFIG_FIREWALL
 	/*
 	 *	Check the firewall [well spotted Olaf]
 	 */
 	 
-	if((err=ip_fw_chk(skb->ip_hdr,dev,ip_fw_blk_chain, ip_fw_blk_policy,0))<FW_ACCEPT)
+	if((err=call_in_firewall(PF_INET, skb, skb->ip_hdr))<FW_ACCEPT)
 	{
 		if(err==FW_REJECT)
 			icmp_send(skb,ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0 , dev);
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v1.3.35/linux/net/ipv4/packet.c	Wed Aug  2 13:21:18 1995
+++ linux/net/ipv4/packet.c	Tue Oct 17 13:42:37 1995
@@ -28,7 +28,8 @@
  *					dubious gcc output. Can you read
  *					compiler: it said _VOLATILE_
  *	Richard Kooijman	:	Timestamp fixes.
- *		Alan Cox	:	New buffers. Use sk->mac.raw
+ *		Alan Cox	:	New buffers. Use sk->mac.raw.
+ *		Alan Cox	:	sendmsg/recvmsg support.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -73,7 +74,6 @@
 int packet_rcv(struct sk_buff *skb, struct device *dev,  struct packet_type *pt)
 {
 	struct sock *sk;
-	unsigned long flags;
 	
 	/*
 	 *	When we registered the protocol we saved the socket in the data
@@ -83,7 +83,8 @@
 	sk = (struct sock *) pt->data;	
 	
 	/*
-	 *	Yank back the headers
+	 *	Yank back the headers [hope the device set this
+	 *	right or kerboom...]
 	 */
 	 
 	skb_push(skb,skb->data-skb->mac.raw);
@@ -99,35 +100,13 @@
 	 *	to prevent sockets using all the memory up.
 	 */
 	 
-	if (sk->rmem_alloc & 0xFF000000) {
-		printk("packet_rcv: sk->rmem_alloc = %ld\n", sk->rmem_alloc);
-		sk->rmem_alloc = 0;
-	}
-
-	if (sk->rmem_alloc + skb->truesize >= sk->rcvbuf) 
+	if(sock_queue_rcv_skb(sk,skb)<0)
 	{
-/*	        printk("packet_rcv: drop, %d+%d>%d\n", sk->rmem_alloc, skb->truesize, sk->rcvbuf); */
 		skb->sk = NULL;
 		kfree_skb(skb, FREE_READ);
-		return(0);
+		release_sock(sk);		
+		return 0;
 	}
-
-	save_flags(flags);
-	cli();
-
-	skb->sk = sk;
-	sk->rmem_alloc += skb->truesize;	
-
-	/*
-	 *	Queue the packet up, and wake anyone waiting for it.
-	 */
-
-	skb_queue_tail(&sk->receive_queue,skb);
-	if(!sk->dead)
-		sk->data_ready(sk,skb->len);
-		
-	restore_flags(flags);
-
 	/*
 	 *	Processing complete.
 	 */
@@ -141,13 +120,12 @@
  *	protocol layers and you must therefore supply it with a complete frame
  */
  
-static int packet_sendto(struct sock *sk, const unsigned char *from, int len,
-	      int noblock, unsigned flags, struct sockaddr_in *usin,
-	      int addr_len)
+static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len,
+	      int noblock, int flags)
 {
 	struct sk_buff *skb;
 	struct device *dev;
-	struct sockaddr *saddr=(struct sockaddr *)usin;
+	struct sockaddr *saddr=(struct sockaddr *)msg->msg_name;
 
 	/*
 	 *	Check the flags. 
@@ -160,13 +138,13 @@
 	 *	Get and verify the address. 
 	 */
 	 
-	if (usin) 
+	if (saddr) 
 	{
-		if (addr_len < sizeof(*saddr)) 
+		if (msg->msg_namelen < sizeof(*saddr)) 
 			return(-EINVAL);
 	} 
 	else
-		return(-EINVAL);	/* SOCK_PACKET must be sent giving an address */
+		return(-ENOTCONN);	/* SOCK_PACKET must be sent giving an address */
 	
 	/*
 	 *	Find the device first to size check it 
@@ -176,7 +154,7 @@
 	dev = dev_get(saddr->sa_data);
 	if (dev == NULL) 
 	{
-		return(-ENXIO);
+		return(-ENODEV);
   	}
 	
 	/*
@@ -187,11 +165,12 @@
 	if(len>dev->mtu+dev->hard_header_len)
   		return -EMSGSIZE;
 
-	skb = sk->prot->wmalloc(sk, len, 0, GFP_KERNEL);
+	skb = sock_wmalloc(sk, len, 0, GFP_KERNEL);
 
 	/*
 	 *	If the write buffer is full, then tough. At this level the user gets to
-	 *	deal with the problem - do your own algorithmic backoffs.
+	 *	deal with the problem - do your own algorithmic backoffs. Thats far
+	 *	more flexible.
 	 */
 	 
 	if (skb == NULL) 
@@ -205,7 +184,7 @@
 	 
 	skb->sk = sk;
 	skb->free = 1;
-	memcpy_fromfs(skb_put(skb,len), from, len);
+	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	skb->arp = 1;		/* No ARP needs doing on this (complete) frame */
 
 	/*
@@ -219,6 +198,26 @@
 	return(len);
 }
 
+static int packet_sendto(struct sock *sk, const unsigned char *from, int len,
+	      int noblock, unsigned flags, struct sockaddr_in *usin,
+	      int addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = (void *)from;
+	iov.iov_len  = len;
+
+	msg.msg_name      = (void *)usin;
+	msg.msg_namelen   = addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return packet_sendmsg(sk, &msg, len, noblock, flags);
+}
+
+
 /*
  *	A write to a SOCK_PACKET can't actually do anything useful and will
  *	always fail but we include it for completeness and future expansion.
@@ -284,17 +283,14 @@
  *	If necessary we block.
  */
  
-int packet_recvfrom(struct sock *sk, unsigned char *to, int len,
-	        int noblock, unsigned flags, struct sockaddr_in *sin,
-	        int *addr_len)
+int packet_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+	        int noblock, int flags,int *addr_len)
 {
 	int copied=0;
 	struct sk_buff *skb;
-	struct sockaddr *saddr;
+	struct sockaddr *saddr=(struct sockaddr *)msg->msg_name;
 	int err;
 
-	saddr = (struct sockaddr *)sin;
-
 	if (sk->shutdown & RCV_SHUTDOWN) 
 		return(0);
 		
@@ -330,7 +326,7 @@
 	 
 	copied = min(len, skb->len);
 
-	memcpy_tofs(to, skb->data, copied);	/* We can't use skb_copy_datagram here */
+	memcpy_toiovec(msg->msg_iov, skb->data, copied);	/* We can't use skb_copy_datagram here */
 	sk->stamp=skb->stamp;
 
 	/*
@@ -358,6 +354,27 @@
 	return(copied);
 }
 
+static int packet_recvfrom(struct sock *sk, unsigned char *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr_in *sa, int *addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = ubuf;
+	iov.iov_len  = size;
+
+	msg.msg_name      = (void *)sa;
+	msg.msg_namelen   = 0;
+	if (addr_len)
+		msg.msg_namelen = *addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return packet_recvmsg(sk, &msg, size, noblock, flags, addr_len);
+}
+
+
 
 /*
  *	A packet read can succeed and is just the same as a recvfrom but without the
@@ -379,12 +396,6 @@
  
 struct proto packet_prot = 
 {
-	sock_wmalloc,
-	sock_rmalloc,
-	sock_wfree,
-	sock_rfree,
-	sock_rspace,
-	sock_wspace,
 	packet_close,
 	packet_read,
 	packet_write,
@@ -399,11 +410,13 @@
 	NULL,
 	NULL, 
 	datagram_select,
-	NULL,
+	NULL,			/* No ioctl */
 	packet_init,
 	NULL,
 	NULL,			/* No set/get socket options */
 	NULL,
+	packet_sendmsg,		/* Sendmsg */
+	packet_recvmsg,		/* Recvmsg */
 	128,
 	0,
 	"PACKET",
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v1.3.35/linux/net/ipv4/raw.c	Wed Sep 27 16:00:02 1995
+++ linux/net/ipv4/raw.c	Tue Oct 17 13:42:37 1995
@@ -266,13 +266,13 @@
  *	we return it, otherwise we block.
  */
 
-int raw_recvfrom(struct sock *sk, unsigned char *to, int len,
-     int noblock, unsigned flags, struct sockaddr_in *sin,
-	     int *addr_len)
+int raw_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+     int noblock, int flags,int *addr_len)
 {
 	int copied=0;
 	struct sk_buff *skb;
 	int err;
+	struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
 
 	if (flags & MSG_OOB)
 		return -EOPNOTSUPP;
@@ -289,7 +289,7 @@
 
 	copied = min(len, skb->len);
 	
-	skb_copy_datagram(skb, 0, to, copied);
+	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	sk->stamp=skb->stamp;
 
 	/* Copy the address. */
@@ -304,19 +304,33 @@
 }
 
 
-int raw_read (struct sock *sk, unsigned char *buff, int len, int noblock,unsigned flags)
+static int raw_recvfrom(struct sock *sk, unsigned char *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr_in *sa, int *addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = ubuf;
+	iov.iov_len  = size;
+
+	msg.msg_name      = (void *)sa;
+	msg.msg_namelen   = 0;
+	if (addr_len)
+		msg.msg_namelen = *addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return raw_recvmsg(sk, &msg, size, noblock, flags, addr_len);
+}
+
+int raw_read (struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags)
 {
 	return(raw_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
 }
 
 
 struct proto raw_prot = {
-	sock_wmalloc,
-	sock_rmalloc,
-	sock_wfree,
-	sock_rfree,
-	sock_rspace,
-	sock_wspace,
 	raw_close,
 	raw_read,
 	raw_write,
@@ -340,6 +354,8 @@
 	NULL,
 	ip_setsockopt,
 	ip_getsockopt,
+	NULL,
+	raw_recvmsg,
 	128,
 	0,
 	"RAW",
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.35/linux/net/ipv4/tcp.c	Wed Oct  4 14:14:35 1995
+++ linux/net/ipv4/tcp.c	Tue Oct 17 13:42:37 1995
@@ -530,7 +530,7 @@
    
 int tcp_select_window(struct sock *sk)
 {
-	int new_window = sk->prot->rspace(sk);
+	int new_window = sock_rspace(sk);
 	
 	if(sk->window_clamp)
 		new_window=min(sk->window_clamp,new_window);
@@ -1227,7 +1227,7 @@
 		 * by Matt Dillon.
 		 */
 
-		if (sk->prot->wspace(sk) < sk->mtu+128+sk->prot->max_header)
+		if (sock_wspace(sk) < sk->mtu+128+sk->prot->max_header)
 			break;
 		return 1;
 
@@ -1280,7 +1280,7 @@
 			unsigned long amount;
 
 			if (sk->state == TCP_LISTEN) return(-EINVAL);
-			amount = sk->prot->wspace(sk);
+			amount = sock_wspace(sk);
 			err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(int));
 			if(err)
 				return err;
@@ -1521,7 +1521,7 @@
 	 * and then put it into the queue to be sent.
 	 */
 
-	buff = sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
+	buff = sock_wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
 	if (buff == NULL) 
 	{
 		/* 
@@ -1555,7 +1555,7 @@
 	if (tmp < 0) 
 	{
   		buff->free = 1;
-		sk->prot->wfree(sk, buff);
+		sock_wfree(sk, buff);
 		return;
 	}
 	t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
@@ -1644,53 +1644,55 @@
  *	and starts the transmit system.
  */
 
-static int tcp_write(struct sock *sk, const unsigned char *from,
-	  int len, int nonblock, unsigned flags)
+static int tcp_sendmsg(struct sock *sk, struct msghdr *msg,
+	  int len, int nonblock, int flags)
 {
 	int copied = 0;
 	int copy;
 	int tmp;
+	int seglen;
+	int iovct=0;
 	struct sk_buff *skb;
 	struct sk_buff *send_tmp;
 	struct proto *prot;
 	struct device *dev = NULL;
-
-	sk->inuse=1;
-	prot = sk->prot;
-	while(len > 0) 
+	unsigned char *from;
+	
+	/*
+	 *	Do sanity checking for sendmsg/sendto/send
+	 */
+	 
+	if (flags & ~(MSG_OOB|MSG_DONTROUTE))
+		return -EINVAL;
+	if (msg->msg_name)
 	{
-		if (sk->err) 
-		{			/* Stop on an error */
-			release_sock(sk);
-			if (copied) 
-				return(copied);
-			tmp = -sk->err;
-			sk->err = 0;
-			return(tmp);
-		}
-
-		/*
-		 *	First thing we do is make sure that we are established. 
-		 */
+		struct sockaddr_in *addr=(struct sockaddr_in *)msg->msg_name;
+		if(sk->state == TCP_CLOSE)
+			return -ENOTCONN;
+		if (msg->msg_namelen < sizeof(*addr))
+			return -EINVAL;
+		if (addr->sin_family && addr->sin_family != AF_INET) 
+			return -EINVAL;
+		if (addr->sin_port != sk->dummy_th.dest) 
+			return -EISCONN;
+		if (addr->sin_addr.s_addr != sk->daddr) 
+			return -EISCONN;
+	}
 	
-		if (sk->shutdown & SEND_SHUTDOWN) 
-		{
-			release_sock(sk);
-			sk->err = EPIPE;
-			if (copied) 
-				return(copied);
-			sk->err = 0;
-			return(-EPIPE);
-		}
-
-		/* 
-		 *	Wait for a connection to finish.
-		 */
+	/*
+	 *	Ok commence sending
+	 */
 	
-		while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) 
+	while(iovct<msg->msg_iovlen)
+	{
+		seglen=msg->msg_iov[iovct].iov_len;
+		from=msg->msg_iov[iovct++].iov_base;
+		sk->inuse=1;
+		prot = sk->prot;
+		while(seglen > 0) 
 		{
 			if (sk->err) 
-			{
+			{			/* Stop on an error */
 				release_sock(sk);
 				if (copied) 
 					return(copied);
@@ -1699,244 +1701,277 @@
 				return(tmp);
 			}
 
-			if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) 
+			/*
+			 *	First thing we do is make sure that we are established. 
+			 */
+	
+			if (sk->shutdown & SEND_SHUTDOWN) 
 			{
 				release_sock(sk);
+				sk->err = EPIPE;
 				if (copied) 
 					return(copied);
+				sk->err = 0;
+				return(-EPIPE);
+			}
 
+			/* 
+			 *	Wait for a connection to finish.
+			 */
+		
+			while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) 
+			{
 				if (sk->err) 
 				{
+					release_sock(sk);
+					if (copied) 
+						return(copied);
 					tmp = -sk->err;
 					sk->err = 0;
 					return(tmp);
-				}
-
-				if (sk->keepopen) 
+				}		
+	
+				if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) 
 				{
-					send_sig(SIGPIPE, current, 0);
-				}
-				return(-EPIPE);
-			}
-
-			if (nonblock || copied) 
-			{
-				release_sock(sk);
-				if (copied) 
-					return(copied);
-				return(-EAGAIN);
-			}
+					release_sock(sk);
+					if (copied) 
+						return(copied);
+	
+					if (sk->err) 
+					{	
+						tmp = -sk->err;
+						sk->err = 0;
+						return(tmp);
+					}
 
-			release_sock(sk);
-			cli();
-		
-			if (sk->state != TCP_ESTABLISHED &&
-		    		sk->state != TCP_CLOSE_WAIT && sk->err == 0) 
-		    	{
-				interruptible_sleep_on(sk->sleep);
-				if (current->signal & ~current->blocked) 
+					if (sk->keepopen) 
+					{
+						send_sig(SIGPIPE, current, 0);
+					}
+					return(-EPIPE);
+				}
+	
+				if (nonblock || copied) 
 				{
-					sti();
+					release_sock(sk);
 					if (copied) 
 						return(copied);
-					return(-ERESTARTSYS);
+					return(-EAGAIN);
+				}
+	
+				release_sock(sk);
+				cli();
+			
+				if (sk->state != TCP_ESTABLISHED &&
+			    		sk->state != TCP_CLOSE_WAIT && sk->err == 0) 
+			    	{
+					interruptible_sleep_on(sk->sleep);	
+					if (current->signal & ~current->blocked)
+					{
+						sti();
+						if (copied) 
+							return(copied);
+						return(-ERESTARTSYS);
+					}
 				}
+				sk->inuse = 1;
+				sti();
 			}
-			sk->inuse = 1;
-			sti();
-		}
-
-	/*
-	 * The following code can result in copy <= if sk->mss is ever
-	 * decreased.  It shouldn't be.  sk->mss is min(sk->mtu, sk->max_window).
-	 * sk->mtu is constant once SYN processing is finished.  I.e. we
-	 * had better not get here until we've seen his SYN and at least one
-	 * valid ack.  (The SYN sets sk->mtu and the ack sets sk->max_window.)
-	 * But ESTABLISHED should guarantee that.  sk->max_window is by definition
-	 * non-decreasing.  Note that any ioctl to set user_mss must be done
-	 * before the exchange of SYN's.  If the initial ack from the other
-	 * end has a window of 0, max_window and thus mss will both be 0.
-	 */
-
-	/* 
-	 *	Now we need to check if we have a half built packet. 
-	 */
-
-		if ((skb = tcp_dequeue_partial(sk)) != NULL) 
-		{
-			int hdrlen;
-
-			 /* IP header + TCP header */
-			hdrlen = ((unsigned long)skb->h.th - (unsigned long)skb->data)
-				 + sizeof(struct tcphdr);
 	
-			/* Add more stuff to the end of skb->len */
-			if (!(flags & MSG_OOB)) 
+		/*
+		 * The following code can result in copy <= if sk->mss is ever
+		 * decreased.  It shouldn't be.  sk->mss is min(sk->mtu, sk->max_window).
+		 * sk->mtu is constant once SYN processing is finished.  I.e. we
+		 * had better not get here until we've seen his SYN and at least one
+		 * valid ack.  (The SYN sets sk->mtu and the ack sets sk->max_window.)
+		 * But ESTABLISHED should guarantee that.  sk->max_window is by definition
+		 * non-decreasing.  Note that any ioctl to set user_mss must be done
+		 * before the exchange of SYN's.  If the initial ack from the other
+		 * end has a window of 0, max_window and thus mss will both be 0.
+		 */
+	
+		/* 
+		 *	Now we need to check if we have a half built packet. 
+		 */
+	
+			if ((skb = tcp_dequeue_partial(sk)) != NULL) 
 			{
-				copy = min(sk->mss - (skb->len - hdrlen), len);
-				/* FIXME: this is really a bug. */
-				if (copy <= 0) 
+				int hdrlen;
+
+				 /* IP header + TCP header */
+				hdrlen = ((unsigned long)skb->h.th - (unsigned long)skb->data)
+					 + sizeof(struct tcphdr);
+	
+				/* Add more stuff to the end of skb->len */
+				if (!(flags & MSG_OOB)) 
 				{
-			  		printk("TCP: **bug**: \"copy\" <= 0!!\n");
-			  		copy = 0;
+					copy = min(sk->mss - (skb->len - hdrlen), len);
+					/* FIXME: this is really a bug. */
+					if (copy <= 0) 
+					{
+				  		printk("TCP: **bug**: \"copy\" <= 0!!\n");
+				  		copy = 0;
+					}		  
+					memcpy_fromfs(skb_put(skb,copy), from, copy);
+					from += copy;
+					copied += copy;
+					len -= copy;
+					seglen -= copy;
+					sk->write_seq += copy;
+					seglen -= copy;
 				}
-	  
-				memcpy_fromfs(skb_put(skb,copy), from, copy);
-				from += copy;
-				copied += copy;
-				len -= copy;
-				sk->write_seq += copy;
+				if ((skb->len - hdrlen) >= sk->mss ||
+					(flags & MSG_OOB) || !sk->packets_out)
+					tcp_send_skb(sk, skb);
+				else
+					tcp_enqueue_partial(skb, sk);
+				continue;
 			}
-			if ((skb->len - hdrlen) >= sk->mss ||
-				(flags & MSG_OOB) || !sk->packets_out)
-				tcp_send_skb(sk, skb);
-			else
-				tcp_enqueue_partial(skb, sk);
-			continue;
-		}
 
-	/*
-	 * We also need to worry about the window.
- 	 * If window < 1/2 the maximum window we've seen from this
- 	 *   host, don't use it.  This is sender side
- 	 *   silly window prevention, as specified in RFC1122.
- 	 *   (Note that this is different than earlier versions of
- 	 *   SWS prevention, e.g. RFC813.).  What we actually do is 
-	 *   use the whole MSS.  Since the results in the right
-	 *   edge of the packet being outside the window, it will
-	 *   be queued for later rather than sent.
-	 */
-
-		copy = sk->window_seq - sk->write_seq;
-		if (copy <= 0 || copy < (sk->max_window >> 1) || copy > sk->mss)
-			copy = sk->mss;
-		if (copy > len)
-			copy = len;
-
-	/*
-	 *	We should really check the window here also. 
-	 */
-	 
-		send_tmp = NULL;
-		if (copy < sk->mss && !(flags & MSG_OOB)) 
-		{
-			/*
-			 *	We will release the socket in case we sleep here. 
-			 */
-			release_sock(sk);
-			/*
-			 *	NB: following must be mtu, because mss can be increased.
-			 *	mss is always <= mtu 
-			 */
-			skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header + 15, 0, GFP_KERNEL);
-			sk->inuse = 1;
-			send_tmp = skb;
-		} 
-		else 
-		{
-			/*
-			 *	We will release the socket in case we sleep here. 
-			 */
-			release_sock(sk);
-			skb = prot->wmalloc(sk, copy + prot->max_header + 15 , 0, GFP_KERNEL);
-  			sk->inuse = 1;
-		}
+		/*
+		 * We also need to worry about the window.
+	 	 * If window < 1/2 the maximum window we've seen from this
+	 	 *   host, don't use it.  This is sender side
+	 	 *   silly window prevention, as specified in RFC1122.
+	 	 *   (Note that this is different than earlier versions of
+		 *   SWS prevention, e.g. RFC813.).  What we actually do is 
+		 *   use the whole MSS.  Since the results in the right
+		 *   edge of the packet being outside the window, it will
+		 *   be queued for later rather than sent.
+		 */
+
+			copy = sk->window_seq - sk->write_seq;
+			if (copy <= 0 || copy < (sk->max_window >> 1) || copy > sk->mss)
+				copy = sk->mss;
+			if (copy > len)
+				copy = len;
 
 		/*
-		 *	If we didn't get any memory, we need to sleep. 
+		 *	We should really check the window here also. 
 		 */
-
-		if (skb == NULL) 
-		{
-			sk->socket->flags |= SO_NOSPACE;
-			if (nonblock) 
+		 
+			send_tmp = NULL;
+			if (copy < sk->mss && !(flags & MSG_OOB)) 
 			{
+				/*
+				 *	We will release the socket in case we sleep here. 
+				 */
 				release_sock(sk);
-				if (copied) 
-					return(copied);
-				return(-EAGAIN);
+				/*
+				 *	NB: following must be mtu, because mss can be increased.
+				 *	mss is always <= mtu 
+				 */
+				skb = sock_wmalloc(sk, sk->mtu + 128 + prot->max_header + 15, 0, GFP_KERNEL);
+				sk->inuse = 1;
+				send_tmp = skb;
+			} 
+			else 
+			{
+				/*
+				 *	We will release the socket in case we sleep here. 
+				 */
+				release_sock(sk);
+				skb = sock_wmalloc(sk, copy + prot->max_header + 15 , 0, GFP_KERNEL);
+  				sk->inuse = 1;
 			}
-
-			/*
-			 *	FIXME: here is another race condition. 
-			 */
-
-			tmp = sk->wmem_alloc;
-			release_sock(sk);
-			cli();
+	
 			/*
-			 *	Again we will try to avoid it. 
+			 *	If we didn't get any memory, we need to sleep. 
 			 */
-			if (tmp <= sk->wmem_alloc &&
-				  (sk->state == TCP_ESTABLISHED||sk->state == TCP_CLOSE_WAIT)
-				&& sk->err == 0) 
+	
+			if (skb == NULL) 
 			{
-				sk->socket->flags &= ~SO_NOSPACE;
-				interruptible_sleep_on(sk->sleep);
-				if (current->signal & ~current->blocked) 
+				sk->socket->flags |= SO_NOSPACE;
+				if (nonblock) 
 				{
-					sti();
+					release_sock(sk);
 					if (copied) 
 						return(copied);
-					return(-ERESTARTSYS);
+					return(-EAGAIN);
 				}
+
+				/*
+				 *	FIXME: here is another race condition. 
+				 */
+
+				tmp = sk->wmem_alloc;
+				release_sock(sk);
+				cli();
+				/*
+				 *	Again we will try to avoid it. 
+				 */
+				if (tmp <= sk->wmem_alloc &&
+					  (sk->state == TCP_ESTABLISHED||sk->state == TCP_CLOSE_WAIT)
+					&& sk->err == 0) 
+				{
+					sk->socket->flags &= ~SO_NOSPACE;
+					interruptible_sleep_on(sk->sleep);
+					if (current->signal & ~current->blocked) 
+					{
+						sti();
+						if (copied) 
+							return(copied);
+						return(-ERESTARTSYS);
+					}
+				}
+				sk->inuse = 1;
+				sti();
+				continue;
 			}
-			sk->inuse = 1;
-			sti();
-			continue;
-		}
 
-		skb->sk = sk;
-		skb->free = 0;
-		skb->localroute = sk->localroute|(flags&MSG_DONTROUTE);
+			skb->sk = sk;
+			skb->free = 0;
+			skb->localroute = sk->localroute|(flags&MSG_DONTROUTE);
 	
-		/*
-		 * FIXME: we need to optimize this.
-		 * Perhaps some hints here would be good.
-		 */
+			/*
+			 * FIXME: we need to optimize this.
+			 * Perhaps some hints here would be good.
+			 */
 		
-		tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev,
+			tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev,
 				 IPPROTO_TCP, sk->opt, skb->truesize,sk->ip_tos,sk->ip_ttl);
-		if (tmp < 0 ) 
-		{
-			prot->wfree(sk, skb);
-			release_sock(sk);
-			if (copied) 
-				return(copied);
-			return(tmp);
-		}
-		skb->dev = dev;
-		skb->h.th =(struct tcphdr *)skb_put(skb,sizeof(struct tcphdr));
-		tmp = tcp_build_header(skb->h.th, sk, len-copy);
-		if (tmp < 0) 
-		{
-			prot->wfree(sk, skb);
-			release_sock(sk);
-			if (copied) 
-				return(copied);
-			return(tmp);
-		}
-
-		if (flags & MSG_OOB) 
-		{
-			skb->h.th->urg = 1;
-			skb->h.th->urg_ptr = ntohs(copy);
-		}
+			if (tmp < 0 ) 
+			{
+				sock_wfree(sk, skb);
+				release_sock(sk);
+				if (copied) 
+					return(copied);
+				return(tmp);
+			}
+			skb->dev = dev;
+			skb->h.th =(struct tcphdr *)skb_put(skb,sizeof(struct tcphdr));
+			tmp = tcp_build_header(skb->h.th, sk, len-copy);
+			if (tmp < 0) 
+			{
+				sock_wfree(sk, skb);
+				release_sock(sk);
+				if (copied) 
+					return(copied);
+				return(tmp);
+			}
+	
+			if (flags & MSG_OOB) 
+			{
+				skb->h.th->urg = 1;
+				skb->h.th->urg_ptr = ntohs(copy);
+			}
 
-		memcpy_fromfs(skb_put(skb,copy), from, copy);
+			memcpy_fromfs(skb_put(skb,copy), from, copy);
 		
-		from += copy;
-		copied += copy;
-		len -= copy;
-		skb->free = 0;
-		sk->write_seq += copy;
-	
-		if (send_tmp != NULL && sk->packets_out) 
-		{
-			tcp_enqueue_partial(send_tmp, sk);
-			continue;
+			from += copy;
+			copied += copy;
+			len -= copy;
+			seglen -= copy;
+			skb->free = 0;
+			sk->write_seq += copy;
+		
+			if (send_tmp != NULL && sk->packets_out) 
+			{
+				tcp_enqueue_partial(send_tmp, sk);
+				continue;
+			}
+			tcp_send_skb(sk, skb);
 		}
-		tcp_send_skb(sk, skb);
 	}
 	sk->err = 0;
 
@@ -1961,27 +1996,27 @@
 	return(copied);
 }
 
-/*
- *	This is just a wrapper. 
- */
+static int tcp_sendto(struct sock *sk, const unsigned char *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr_in *sin, int addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = (void *)ubuf;
+	iov.iov_len  = size;
 
-static int tcp_sendto(struct sock *sk, const unsigned char *from,
-	   int len, int nonblock, unsigned flags,
-	   struct sockaddr_in *addr, int addr_len)
+	msg.msg_name      = (void *)sin;
+	msg.msg_namelen   = addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return tcp_sendmsg(sk, &msg, size, noblock, flags);
+}
+
+static int tcp_write(struct sock *sk, const unsigned char *ubuf, int size, int noblock, unsigned flags)
 {
-	if (flags & ~(MSG_OOB|MSG_DONTROUTE))
-		return -EINVAL;
-	if (sk->state == TCP_CLOSE)
-		return -ENOTCONN;
-	if (addr_len < sizeof(*addr))
-		return -EINVAL;
-	if (addr->sin_family && addr->sin_family != AF_INET) 
-		return -EINVAL;
-	if (addr->sin_port != sk->dummy_th.dest) 
-		return -EISCONN;
-	if (addr->sin_addr.s_addr != sk->daddr) 
-		return -EISCONN;
-	return tcp_write(sk, from, len, nonblock, flags);
+	return tcp_sendto(sk,ubuf,size,noblock,flags,NULL,0);
 }
 
 
@@ -2018,7 +2053,7 @@
 	 * and then put it into the queue to be sent.
 	 */
 
-	buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
+	buff = sock_wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
 	if (buff == NULL) 
 	{
 		/* Try again real soon. */
@@ -2038,7 +2073,7 @@
 	if (tmp < 0) 
 	{
   		buff->free = 1;
-		sk->prot->wfree(sk, buff);
+		sock_wfree(sk, buff);
 		return;
 	}
 
@@ -2085,7 +2120,7 @@
 	save_flags(flags);
 	cli();
   
-	left = sk->prot->rspace(sk);
+	left = sock_rspace(sk);
  
 	/*
 	 *	We have to loop through all the buffer headers,
@@ -2111,9 +2146,9 @@
 	 */
 
 	if(sk->debug)
-		printk("sk->rspace = %lu, was %lu\n", sk->prot->rspace(sk),
+		printk("sk->rspace = %lu, was %lu\n", sock_rspace(sk),
   					    left);
-	if ((rspace=sk->prot->rspace(sk)) != left) 
+	if ((rspace=sock_rspace(sk)) != left) 
 	{
 		/*
 		 * This area has caused the most trouble.  The current strategy
@@ -2160,8 +2195,8 @@
  *	this, no blocking and very strange errors 8)
  */
  
-static int tcp_read_urg(struct sock * sk, int nonblock,
-	     unsigned char *to, int len, unsigned flags)
+static int tcp_recv_urg(struct sock * sk, int nonblock,
+	     struct msghdr *msg, int len, int flags, int *addr_len)
 {
 	/*
 	 *	No URG data to read
@@ -2178,7 +2213,8 @@
 
 	if (sk->state == TCP_CLOSE || sk->done) 
 	{
-		if (!sk->done) {
+		if (!sk->done) 
+		{
 			sk->done = 1;
 			return 0;
 		}
@@ -2196,7 +2232,16 @@
 		char c = sk->urg_data;
 		if (!(flags & MSG_PEEK))
 			sk->urg_data = URG_READ;
-		put_fs_byte(c, to);
+		memcpy_toiovec(msg->msg_iov, &c, 1);
+		if(msg->msg_name)
+		{
+			struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
+			sin->sin_family=AF_INET;
+			sin->sin_addr.s_addr=sk->daddr;
+			sin->sin_port=sk->dummy_th.dest;
+		}
+		if(addr_len)
+			*addr_len=sizeof(struct sockaddr_in);
 		release_sock(sk);
 		return 1;
 	}
@@ -2217,8 +2262,8 @@
  *	This routine copies from a sock struct into the user buffer. 
  */
  
-static int tcp_read(struct sock *sk, unsigned char *to,
-	int len, int nonblock, unsigned flags)
+static int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
+	int len, int nonblock, int flags, int *addr_len)
 {
 	struct wait_queue wait = { current, NULL };
 	int copied = 0;
@@ -2238,7 +2283,7 @@
 	 */
 	 
 	if (flags & MSG_OOB)
-		return tcp_read_urg(sk, nonblock, to, len, flags);
+		return tcp_recv_urg(sk, nonblock, msg, len, flags, addr_len);
 
 	/*
 	 *	Copying sequence to update. This is volatile to handle
@@ -2391,11 +2436,10 @@
 		 *	a crash when cleanup_rbuf() gets called.
 		 */
 		 
-		memcpy_tofs(to,((unsigned char *)skb->h.th) +
+		memcpy_toiovec(msg->msg_iov,((unsigned char *)skb->h.th) +
 			skb->h.th->doff*4 + offset, used);
 		copied += used;
 		len -= used;
-		to += used;
 		
 		/*
 		 *	We now will not sleep again until we are finished
@@ -2435,6 +2479,17 @@
 		break;
 
 	}
+	
+	if(copied>0 && msg->msg_name)
+	{
+		struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
+		sin->sin_family=AF_INET;
+		sin->sin_addr.s_addr=sk->daddr;
+		sin->sin_port=sk->dummy_th.dest;
+	}
+	if(addr_len)
+		*addr_len=sizeof(struct sockaddr_in);
+		
 	remove_wait_queue(sk->sleep, &wait);
 	current->state = TASK_RUNNING;
 
@@ -2444,6 +2499,34 @@
 	return copied;
 }
 
+
+static int tcp_recvfrom(struct sock *sk, unsigned char *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr_in *sa, int *addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = (void *)ubuf;
+	iov.iov_len  = size;
+
+	msg.msg_name      = (void *)sa;
+	msg.msg_namelen   = 0;
+	if (addr_len)
+		msg.msg_namelen = *addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return tcp_recvmsg(sk, &msg, size, noblock, flags, addr_len);
+}
+
+int tcp_read(struct sock *sk, unsigned char *buff, int len, int noblock,
+	 unsigned flags)
+{
+	return(tcp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+}
+
+
 /*
  *	State processing on a close. This implements the state shift for
  *	sending our FIN frame. Note that we only send a FIN for some 
@@ -2516,7 +2599,7 @@
 		
 	release_sock(sk); /* in case the malloc sleeps. */
 	
-	buff = prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL);
+	buff = sock_wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL);
 	sk->inuse = 1;
 
 	if (buff == NULL)
@@ -2549,7 +2632,7 @@
   		 */
   		 
 	  	buff->free = 1;
-		prot->wfree(sk,buff);
+		sock_wfree(sk,buff);
 		sk->write_seq++;
 		t=del_timer(&sk->timer);
 		if(t)
@@ -2657,37 +2740,6 @@
 	release_sock(sk);
 }
 
-
-static int
-tcp_recvfrom(struct sock *sk, unsigned char *to,
-	     int to_len, int nonblock, unsigned flags,
-	     struct sockaddr_in *addr, int *addr_len)
-{
-	int result;
-  
-	/* 
-	 *	Have to check these first unlike the old code. If 
-	 *	we check them after we lose data on an error
-	 *	which is wrong 
-	 */
-
-	if(addr_len)
-		*addr_len = sizeof(*addr);
-	result=tcp_read(sk, to, to_len, nonblock, flags);
-
-	if (result < 0) 
-		return(result);
-  
-  	if(addr)
-  	{
-		addr->sin_family = AF_INET;
- 		addr->sin_port = sk->dummy_th.dest;
-		addr->sin_addr.s_addr = sk->daddr;
-	}
-	return(result);
-}
-
-
 /*
  *	This routine will send an RST to the other tcp. 
  */
@@ -2712,7 +2764,7 @@
 	 * and then put it into the queue to be sent.
 	 */
 
-	buff = prot->wmalloc(NULL, MAX_RESET_SIZE, 1, GFP_ATOMIC);
+	buff = sock_wmalloc(NULL, MAX_RESET_SIZE, 1, GFP_ATOMIC);
 	if (buff == NULL) 
 	  	return;
 
@@ -2729,7 +2781,7 @@
 	if (tmp < 0) 
 	{
   		buff->free = 1;
-		prot->wfree(NULL, buff);
+		sock_wfree(NULL, buff);
 		return;
 	}
 
@@ -3068,7 +3120,7 @@
 	
 	tcp_cache_zap();
 
-	buff = newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
+	buff = sock_wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
 	if (buff == NULL) 
 	{
 		sk->err = ENOMEM;
@@ -4283,7 +4335,7 @@
 	 *	for the send side.  He could be sending us stuff as large as mtu.
 	 */
 		 
-		while (sk->prot->rspace(sk) < sk->mtu) 
+		while (sock_rspace(sk) < sk->mtu) 
 		{
 			skb1 = skb_peek(&sk->receive_queue);
 			if (skb1 == NULL) 
@@ -4511,7 +4563,7 @@
 	sk->dummy_th.dest = usin->sin_port;
 	release_sock(sk);
 
-	buff = sk->prot->wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL);
+	buff = sock_wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL);
 	if (buff == NULL) 
 	{
 		return(-ENOMEM);
@@ -4539,7 +4591,7 @@
 					IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
 	if (tmp < 0) 
 	{
-		sk->prot->wfree(sk, buff);
+		sock_wfree(sk, buff);
 		release_sock(sk);
 		return(-ENETUNREACH);
 	}
@@ -5188,7 +5240,7 @@
 		 *	Grab the data for a temporary frame
 		 */
 		 
-	    	buff = sk->prot->wmalloc(sk, win_size + th->doff * 4 + 
+	    	buff = sock_wmalloc(sk, win_size + th->doff * 4 + 
 				     (iph->ihl << 2) +
 				     sk->prot->max_header + 15, 
 				     1, GFP_ATOMIC);
@@ -5214,7 +5266,7 @@
 					 sk->ip_tos,sk->ip_ttl);
 	    	if (tmp < 0) 
 	    	{
-			sk->prot->wfree(sk, buff);
+			sock_wfree(sk, buff);
 			return;
 		}
 		
@@ -5297,7 +5349,7 @@
 	}
 	else
 	{	
-		buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
+		buff = sock_wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
 		if (buff == NULL) 
 			return;
 
@@ -5313,7 +5365,7 @@
 				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
 		if (tmp < 0) 
 		{
-			sk->prot->wfree(sk, buff);
+			sock_wfree(sk, buff);
 			return;
 		}
 
@@ -5440,12 +5492,6 @@
 
 
 struct proto tcp_prot = {
-	sock_wmalloc,
-	sock_rmalloc,
-	sock_wfree,
-	sock_rfree,
-	sock_rspace,
-	sock_wspace,
 	tcp_close,
 	tcp_read,
 	tcp_write,
@@ -5465,6 +5511,8 @@
 	tcp_shutdown,
 	tcp_setsockopt,
 	tcp_getsockopt,
+	tcp_sendmsg,
+	tcp_recvmsg,
 	128,
 	0,
 	"TCP",
diff -u --recursive --new-file v1.3.35/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v1.3.35/linux/net/ipv4/udp.c	Wed Sep 27 16:00:02 1995
+++ linux/net/ipv4/udp.c	Tue Oct 17 13:42:37 1995
@@ -425,7 +425,7 @@
 			unsigned long amount;
 
 			if (sk->state == TCP_LISTEN) return(-EINVAL);
-			amount = sk->prot->wspace(sk)/*/2*/;
+			amount = sock_wspace(sk);
 			err=verify_area(VERIFY_WRITE,(void *)arg,
 					sizeof(unsigned long));
 			if(err)
@@ -470,14 +470,14 @@
  * 	return it, otherwise we block.
  */
 
-int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
-	     int noblock, unsigned flags, struct sockaddr_in *sin,
-	     int *addr_len)
+int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+	     int noblock, int flags,int *addr_len)
 {
   	int copied = 0;
   	int truesize;
   	struct sk_buff *skb;
   	int er;
+  	struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
 
 	/*
 	 *	Check any passed addresses
@@ -502,7 +502,7 @@
   	 *	FIXME : should use udp header size info value 
   	 */
   	 
-	skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
+	skb_copy_datagram_iovec(skb,sizeof(struct udphdr),msg->msg_iov,copied);
 	sk->stamp=skb->stamp;
 
 	/* Copy the address. */
@@ -518,6 +518,27 @@
   	return(copied);
 }
 
+int udp_recvfrom(struct sock *sk, unsigned char *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr_in *sa, int *addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = ubuf;
+	iov.iov_len  = size;
+
+	msg.msg_name      = (void *)sa;
+	msg.msg_namelen   = 0;
+	if (addr_len)
+		msg.msg_namelen = *addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return udp_recvmsg(sk, &msg, size, noblock, flags, addr_len);
+}
+
+
 /*
  *	Read has the same semantics as recv in SOCK_DGRAM
  */
@@ -739,12 +760,6 @@
 
 
 struct proto udp_prot = {
-	sock_wmalloc,
-	sock_rmalloc,
-	sock_wfree,
-	sock_rfree,
-	sock_rspace,
-	sock_wspace,
 	udp_close,
 	udp_read,
 	udp_write,
@@ -764,6 +779,8 @@
 	NULL,
 	ip_setsockopt,
 	ip_getsockopt,
+	NULL,
+	udp_recvmsg,
 	128,
 	0,
 	"UDP",
diff -u --recursive --new-file v1.3.35/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.35/linux/net/ipx/af_ipx.c	Wed Sep 13 12:45:35 1995
+++ linux/net/ipx/af_ipx.c	Tue Oct 17 13:42:37 1995
@@ -37,8 +37,8 @@
  *			Don't set address length on recvfrom that errors.
  *			Incorrect verify_area.
  *	Revision 0.31:	New sk_buffs. This still needs a lot of testing. <Alan Cox>
- *
- *	TODO:	use sock_alloc_send_skb to allocate sending buffers. Check with Caldera first
+ *	Revision 0.32:  Using sock_alloc_send_skb, firewall hooks. <Alan Cox>
+ *			Supports sendmsg/recvmsg
  *
  * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
  *	Neither Greg Page nor Caldera, Inc. admit liability nor provide 
@@ -73,12 +73,13 @@
 #include <net/psnap.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <linux/firewall.h>
 
 #ifdef CONFIG_IPX
 /* Configuration Variables */
 static unsigned char	ipxcfg_max_hops = 16;
-static char	ipxcfg_auto_select_primary = 0;
-static char	ipxcfg_auto_create_interfaces = 0;
+static char		ipxcfg_auto_select_primary = 0;
+static char		ipxcfg_auto_create_interfaces = 0;
 
 /* Global Variables */
 static struct datalink_proto	*p8022_datalink = NULL;
@@ -560,24 +561,40 @@
 static const char * ipx_device_name(ipx_interface *);
 static int ipxrtr_route_skb(struct sk_buff *);
 
-static int 
-ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
+static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
 {
 	ipx_packet	*ipx = (ipx_packet *) (skb->h.raw);
 	ipx_interface	*i;
 
+#ifdef CONFIG_FIREWALL	
+	/*
+	 *	We firewall first, ask questions later.
+	 */
+	 
+	if (call_in_firewall(PF_IPX, skb, ipx)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	
+#endif	
+
 	/* See if we should update our network number */
 	if ((intrfc->if_netnum == 0L) && 
 		(ipx->ipx_source.net == ipx->ipx_dest.net) &&
-		(ipx->ipx_source.net != 0L)) {
+		(ipx->ipx_source.net != 0L)) 
+	{
 		/* NB: NetWare servers lie about their hop count so we
 		 * dropped the test based on it.  This is the best way
 		 * to determine this is a 0 hop count packet.
 		 */
-		if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL) {
+		if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL) 
+		{
 			intrfc->if_netnum = ipx->ipx_source.net;
 			(void) ipxitf_add_local_route(intrfc);
-		} else {
+		} 
+		else 
+		{
 			printk("IPX: Network number collision %lx\n\t%s %s and %s %s\n",
 				htonl(ipx->ipx_source.net), 
 				ipx_device_name(i),
@@ -592,7 +609,18 @@
 	if (ipx->ipx_source.net == 0L)
 		ipx->ipx_source.net = intrfc->if_netnum;
 
-	if (intrfc->if_netnum != ipx->ipx_dest.net) {
+	if (intrfc->if_netnum != ipx->ipx_dest.net) 
+	{
+#ifdef CONFIG_FIREWALL	
+		/*
+		 *	See if we are allowed to firewall forward
+		 */
+		if (call_fw_firewall(PF_IPX, skb, ipx)!=FW_ACCEPT)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+#endif		
 		/* We only route point-to-point packets. */
 		if ((skb->pkt_type != PACKET_BROADCAST) &&
 			(skb->pkt_type != PACKET_MULTICAST))
@@ -604,7 +632,8 @@
 
 	/* see if we should keep it */
 	if ((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0) 
-		|| (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)) {
+		|| (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)) 
+	{
 		return ipxitf_demux_socket(intrfc, skb, 0);
 	}
 
@@ -988,8 +1017,11 @@
 	return -ENOENT;
 }
 
-static int
-ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, const void *ubuf, int len)
+/*
+ *	Route an outgoing frame from a socket.
+ */
+ 
+static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len)
 {
 	struct sk_buff *skb;
 	ipx_interface *intrfc;
@@ -997,12 +1029,16 @@
 	int size;
 	int ipx_offset;
 	ipx_route *rt = NULL;
-
+	int err;
+	
 	/* Find the appropriate interface on which to send packet */
-	if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL)) {
+	if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL)) 
+	{
 		usipx->sipx_network = ipx_primary_net->if_netnum;
 		intrfc = ipx_primary_net;
-	} else {
+	} 
+	else 
+	{
 		rt = ipxrtr_lookup(usipx->sipx_network);
 		if (rt==NULL) {
 			return -ENETUNREACH;
@@ -1014,12 +1050,10 @@
 	size=sizeof(ipx_packet)+len;
 	size += ipx_offset;
 
-	if(size+sk->wmem_alloc>sk->sndbuf) return -EAGAIN;
-		
-	skb=alloc_skb(size,GFP_KERNEL);
-	if(skb==NULL) return -ENOMEM;
+	skb=sock_alloc_send_skb(sk, size, 0, 0, &err);
+	if(skb==NULL)
+		return err;
 		
-	skb->sk=sk;
 	skb_reserve(skb,ipx_offset);
 	skb->free=1;
 	skb->arp=1;
@@ -1039,7 +1073,16 @@
 	memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN);
 	ipx->ipx_dest.sock=usipx->sipx_port;
 
-	memcpy_fromfs(skb_put(skb,len),ubuf,len);
+	memcpy_fromiovec(skb_put(skb,len),iov,len);
+
+#ifdef CONFIG_FIREWALL	
+	if(call_out_firewall(PF_IPX, skb, ipx)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return -EPERM;
+	}
+#endif
+	
 	return ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ? 
 				rt->ir_router_node : ipx->ipx_dest.node);
 }
@@ -1691,11 +1734,11 @@
 	return ipxitf_rcv(intrfc, skb);
 }
 
-static int ipx_sendto(struct socket *sock, const void *ubuf, int len, int noblock,
-	unsigned flags, struct sockaddr *usip, int addr_len)
+static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock,
+	int flags)
 {
 	ipx_socket *sk=(ipx_socket *)sock->data;
-	struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip;
+	struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)msg->msg_name;
 	struct sockaddr_ipx local_sipx;
 	int retval;
 
@@ -1713,7 +1756,7 @@
 			if (ret != 0) return ret;
 		}
 
-		if(addr_len <sizeof(*usipx))
+		if(msg->msg_namelen <sizeof(*usipx))
 			return -EINVAL;
 		if(usipx->sipx_family != AF_IPX)
 			return -EINVAL;
@@ -1728,22 +1771,40 @@
 		memcpy(usipx->sipx_node,sk->ipx_dest_addr.node,IPX_NODE_LEN);
 	}
 	
-	retval = ipxrtr_route_packet(sk, usipx, ubuf, len);
+	retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len);
 	if (retval < 0) return retval;
 
 	return len;
 }
 
+static int ipx_sendto(struct socket *sock, const void *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr *sa, int addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = (void *)ubuf;
+	iov.iov_len  = size;
+
+	msg.msg_name      = (void *)sa;
+	msg.msg_namelen   = addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return ipx_sendmsg(sock, &msg, size, noblock, flags);
+}
+
 static int ipx_send(struct socket *sock, const void *ubuf, int size, int noblock, unsigned flags)
 {
 	return ipx_sendto(sock,ubuf,size,noblock,flags,NULL,0);
 }
 
-static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
-		   unsigned flags, struct sockaddr *sip, int *addr_len)
+static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
+		 int flags, int *addr_len)
 {
 	ipx_socket *sk=(ipx_socket *)sock->data;
-	struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)sip;
+	struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)msg->msg_name;
 	struct ipx_packet *ipx = NULL;
 	int copied = 0;
 	int truesize;
@@ -1764,13 +1825,14 @@
 	skb=skb_recv_datagram(sk,flags,noblock,&er);
 	if(skb==NULL)
 		return er;
+	
 	if(addr_len)
 		*addr_len=sizeof(*sipx);
 
 	ipx = (ipx_packet *)(skb->h.raw);
 	truesize=ntohs(ipx->ipx_pktsize) - sizeof(ipx_packet);
 	copied = (truesize > size) ? size : truesize;
-	skb_copy_datagram(skb,sizeof(struct ipx_packet),ubuf,copied);
+	skb_copy_datagram_iovec(skb,sizeof(struct ipx_packet),msg->msg_iov,copied);
 	
 	if(sipx)
 	{
@@ -1789,6 +1851,25 @@
 	return ipx_send(sock,ubuf,size,noblock,0);
 }
 
+static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr *sa, int *addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+
+	iov.iov_base = ubuf;
+	iov.iov_len  = size;
+
+	msg.msg_name      = (void *)sa;
+	msg.msg_namelen   = 0;
+	if (addr_len)
+		msg.msg_namelen = *addr_len;
+	msg.msg_accrights = NULL;
+	msg.msg_iov       = &iov;
+	msg.msg_iovlen    = 1;
+
+	return ipx_recvmsg(sock, &msg, size, noblock, flags, addr_len);
+}
 
 static int ipx_recv(struct socket *sock, void *ubuf, int size , int noblock,
 	unsigned flags)
@@ -1915,6 +1996,8 @@
 	ipx_setsockopt,
 	ipx_getsockopt,
 	ipx_fcntl,
+	ipx_sendmsg,
+	ipx_recvmsg
 };
 
 /* Called by ddi.c on kernel start up */
@@ -1990,7 +2073,7 @@
 		ipx_rt_get_info
 	});
 		
-	printk("Swansea University Computer Society IPX 0.31 for NET3.031\n");
+	printk("Swansea University Computer Society IPX 0.33 for NET3.032\n");
 	printk("IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
 }
 #endif
diff -u --recursive --new-file v1.3.35/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v1.3.35/linux/net/netrom/af_netrom.c	Wed Sep 27 16:00:02 1995
+++ linux/net/netrom/af_netrom.c	Tue Oct 17 13:42:37 1995
@@ -913,6 +913,7 @@
 	ax25_address *src, *dest, *user;
 	unsigned short circuit_index, circuit_id;
 	unsigned short frametype, window, timeout;
+	
 
 	skb->sk = NULL;		/* Initially we don't know who its for */
 
@@ -1442,7 +1443,7 @@
 {
 	sock_register(nr_proto_ops.family, &nr_proto_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
-	printk("G4KLX NET/ROM for Linux. Version 0.3 ALPHA for AX25.030 Linux 1.3.25\n");
+	printk("G4KLX NET/ROM for Linux. Version 0.3 ALPHA for AX25.030 Linux 1.3.35\n");
 
 	nr_default.quality    = NR_DEFAULT_QUAL;
 	nr_default.obs_count  = NR_DEFAULT_OBS;
diff -u --recursive --new-file v1.3.35/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v1.3.35/linux/net/netrom/nr_route.c	Wed Aug  9 14:55:45 1995
+++ linux/net/netrom/nr_route.c	Tue Oct 17 13:42:37 1995
@@ -16,6 +16,7 @@
  *	NET/ROM 001	Jonathan(G4KLX)	First attempt.
  *	NET/ROM	003	Jonathan(G4KLX)	Use SIOCADDRT/SIOCDELRT ioctl values
  *					for NET/ROM routes.
+ *			Alan Cox(GW4PTS) Added the firewall hooks.
  *
  *	TO DO
  *	Sort out the which pointer when shuffling entries in the routes
@@ -50,6 +51,7 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/notifier.h>
+#include <linux/firewall.h>
 #include <net/netrom.h>
 
 static int nr_neigh_no = 1;
@@ -644,6 +646,7 @@
  *	Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
  *	indicates an internally generated frame.
  */
+
 int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 {
 	ax25_address *nr_src, *nr_dest;
@@ -651,7 +654,14 @@
 	struct nr_node  *nr_node;
 	struct device *dev;
 	unsigned char *dptr;
+	
+#ifdef CONFIG_FIREWALL
 
+	if(ax25 && call_in_firewall(PF_NETROM, skb, skb->data)!=FW_ACCEPT)
+		return 0;
+	if(!ax25 && call_out_firewall(PF_NETROM, skb, skb->data)!=FW_ACCEPT)
+		return 0;
+#endif
 	nr_src  = (ax25_address *)(skb->data + 0);
 	nr_dest = (ax25_address *)(skb->data + 7);
 
@@ -684,6 +694,11 @@
 
 	if ((dev = nr_dev_first()) == NULL)
 		return 0;
+
+#ifdef CONFIG_FIREWALL
+	if(ax25 && call_fw_firewall(PF_NETROM, skb, skb->data)!=FW_ACCEPT)
+		return 0;
+#endif
 
 	dptr  = skb_push(skb, 1);
 	*dptr = AX25_P_NETROM;
diff -u --recursive --new-file v1.3.35/linux/net/socket.c linux/net/socket.c
--- v1.3.35/linux/net/socket.c	Tue Oct 10 18:46:39 1995
+++ linux/net/socket.c	Tue Oct 17 13:42:37 1995
@@ -63,6 +63,7 @@
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>
 #include <linux/proc_fs.h>
+#include <linux/firewall.h>
 
 #include <net/netlink.h>
 
@@ -1287,7 +1288,7 @@
 {
 	int i;
 
-	printk("Swansea University Computer Society NET3.031 Snap #3 for Linux 1.3.30\n");
+	printk("Swansea University Computer Society NET3.032 for Linux 1.3.35\n");
 
 	/*
 	 *	Initialize all address (protocol) families. 
@@ -1308,6 +1309,14 @@
 
 #if defined(CONFIG_RTNETLINK)
 	netlink_attach(NETLINK_ROUTE, netlink_donothing);
+#endif
+
+	/*
+	 *	Attach the firewall module if configured
+	 */
+	 
+#ifdef CONFIG_FIREWALL	 
+	fwchain_init();
 #endif
 
 	/*
diff -u --recursive --new-file v1.3.35/linux/scripts/Configure linux/scripts/Configure
--- v1.3.35/linux/scripts/Configure	Mon Oct 16 18:38:24 1995
+++ linux/scripts/Configure	Sun Oct 22 16:20:39 1995
@@ -99,11 +99,11 @@
 #
 # bool processes a boolean argument
 #
-#	bool question define default
+#	bool question define
 #
 function bool () {
 	ans=""
-	def=$(eval echo "\${$2:-$3}")
+	def=$(eval echo "\${$2:-'n'}")
         case "$def" in
          "y") defprompt="Y/n"
               ;;
@@ -119,11 +119,11 @@
 #
 # tristate processes a tristate argument
 #
-#	tristate question define default
+#	tristate question define
 #
 function tristate () {
 	ans=""
-	def=$(eval echo "\${$2:-$3}")
+	def=$(eval echo "\${$2:-'n'}")
         case "$def" in
          "y") defprompt="Y/m/n"
               ;;
@@ -149,11 +149,11 @@
 #	else in the kernel.
 #
 function dep_tristate () {
-	if [ "$4" != "m" ]; then
-		tristate "$1" "$2" "$3"
+	def=$(eval echo "\${$2:-'n'}")
+	if [ "$def" != "m" ]; then
+		tristate "$1" "$2"
 	else
 		ans=""
-		def=$(eval echo "\${$2:-$3}")
 	        case "$def" in
         	 "y" | "m") defprompt="M/n"
 		      def="m"
@@ -284,17 +284,26 @@
 	CONFIG_IN=$1
 fi
 
-if [ -f ./.config ] ; then
-	. ./.config
-	sed -e 's/# \(.*\) is not.*/\1=n/' <./.config >/tmp/conf.$$
-	. /tmp/conf.$$
-	rm /tmp/conf.$$
+DEFAULTS=arch/$ARCH/defconfig
+if [ -f .config ]; then
+  DEFAULTS=.config
 fi
-. $CONFIG_IN
 
-case "$CONFIG_SOUND" in
-	[YyMm] )	$MAKE -C drivers/sound config || exit 1 ;;
-esac
+if [ -f $DEFAULTS ]; then
+  echo "#"
+  echo "# Using defaults found in" $DEFAULTS
+  echo "#"
+  . $DEFAULTS
+  sed -e 's/# \(.*\) is not.*/\1=n/' < $DEFAULTS > /tmp/conf.$$
+  . /tmp/conf.$$
+  rm /tmp/conf.$$
+else
+  echo "#"
+  echo "# No defaults found"
+  echo "#"
+fi
+
+. $CONFIG_IN
 
 rm -f .config.old
 if [ -f .config ]; then
diff -u --recursive --new-file v1.3.35/linux/scripts/header.tk linux/scripts/header.tk
--- v1.3.35/linux/scripts/header.tk	Mon Oct 16 18:38:24 1995
+++ linux/scripts/header.tk	Sat Oct 21 12:36:39 1995
@@ -8,6 +8,11 @@
 set modbutton_width 0
 set modbutton_height 0
 
+#
+# Set to be the x and y position of subwindows.
+#
+set winx 100
+set winy 200
 
 #
 # Define some macros we will need to parse the kmenu-config.dat file.
@@ -111,7 +116,8 @@
 
 proc write_config_file  { w } {
 	global loadfile
-	if { [string length $loadfile] != 0 && [file writable $loadfile] == 1 } then {
+	if { [string length $loadfile] != 0 
+    		&& ([file writable $loadfile] == 1 || ([file exists $loadfile] == 0 && [file writable [file dirname $loadfile]] == 1)) } then {
 		writeconfig $loadfile /dev/null
 	} else {
 		catch {destroy $w}
@@ -162,13 +168,16 @@
 proc write_variable { file1 file2 varname variable dep } {
 	if { $variable == 0 } \
 		then { puts $file1 "# $varname is not set"; \
-		       puts $file2 "#undef $varname"}
-	if { $variable == 2 || ($dep == 2 && $variable == 1) } \
+		       puts $file2 "#undef $varname"} \
+	elseif { $variable == 2 || ($dep == 2 && $variable == 1) } \
 		then { puts $file1 "$varname=m"; \
-		       puts $file2 "#undef $varname"  }
-	if { $variable == 1 && $dep != 2 } \
+		       puts $file2 "#undef $varname"  } \
+	elseif { $variable == 1 && $dep != 2 } \
 		then { puts $file1 "$varname=y"; \
-		       puts $file2 "#define $varname 1" }
+		       puts $file2 "#define $varname 1" } \
+	else { \
+	    error "Attempting to write value for variable that is not configured ($varname)." \
+	}
 }
 
 proc bool {w mnum line text variable default} {
diff -u --recursive --new-file v1.3.35/linux/scripts/tail.tk linux/scripts/tail.tk
--- v1.3.35/linux/scripts/tail.tk	Mon Oct 16 18:38:24 1995
+++ linux/scripts/tail.tk	Sat Oct 21 12:36:48 1995
@@ -65,7 +65,3 @@
 			.f0r_bot.save configure -state disabled
 		}
 	}
-
-if { [file writable .config] == 0 || [file writable include/linux/autoconf.h ] == 0 } then {
-	.f0r_bot.save configure -state disabled
-	}
diff -u --recursive --new-file v1.3.35/linux/scripts/tkcond.c linux/scripts/tkcond.c
--- v1.3.35/linux/scripts/tkcond.c	Mon Oct 16 18:38:24 1995
+++ linux/scripts/tkcond.c	Sat Oct 21 12:36:38 1995
@@ -236,6 +236,7 @@
   int depth = 0;
   int i;
   struct kconfig * cfg;
+  struct kconfig * cfg1;
   struct condition * conditions[25];
   struct condition * cnd;
   struct condition * cnd1;
@@ -303,6 +304,43 @@
 	}
     }
   /*
+   * Walk through and see if there are multiple options that control the
+   * same kvariable.  If there are we need to treat them a little bit
+   * special.
+   */
+  for(cfg=scfg;cfg != NULL; cfg = cfg->next)
+    {
+      switch(cfg->tok)
+	{
+	case tok_bool:
+	case tok_tristate:
+	case tok_dep_tristate:
+	case tok_int:
+	  for(cfg1=cfg;cfg1 != NULL; cfg1 = cfg1->next)
+	    {
+	      switch(cfg1->tok)
+		{
+		case tok_bool:
+		case tok_tristate:
+		case tok_dep_tristate:
+		case tok_int:
+		  if( strcmp(cfg->optionname, cfg1->optionname) == 0)
+		    {
+		      cfg->flags |= CFG_DUP;
+		      cfg1->flags |= CFG_DUP;
+		    }
+		  break;
+		default:
+		  break;
+		}
+	    }
+	  break;
+	default:
+	  break;
+	}
+    }
+
+  /*
    * Now go through the list, and every time we see a kvariable, check
    * to see whether it also has some dependencies.  If so, then
    * append it to our list.  The reason we do this is that we might have
@@ -328,6 +366,7 @@
 	  if(cnd->op != op_kvariable) continue;
 	  if(cnd->variable.cfg->cond == NULL) continue;
 
+	  if(cnd->variable.cfg->flags & CFG_DUP) continue; 
 	  /*
 	   * OK, we have some conditions to append to cfg.  Make  a clone
 	   * of the conditions,
diff -u --recursive --new-file v1.3.35/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v1.3.35/linux/scripts/tkgen.c	Mon Oct 16 18:38:24 1995
+++ linux/scripts/tkgen.c	Sat Oct 21 12:36:38 1995
@@ -16,6 +16,18 @@
 #endif
 
 /*
+ * This prevents the Prev/Next buttons from going through the entire sequence
+ * of submenus.  I need to fix the window titles before it would really be
+ * appropriate to enable this.
+ */
+/* #define PREVLAST_LIMITED_RANGE */
+
+/*
+ * This is the total number of submenus that we have.
+ */
+static int tot_menu_num =0;
+
+/*
  * Generate portion of wish script for the beginning of a submenu.
  * The guts get filled in with the various options.
  */
@@ -27,9 +39,9 @@
   printf("\tcatch {destroy $w}\n");
   printf("\ttoplevel $w -class Dialog\n");
   printf("\tmessage $w.m -width 400 -aspect 300 -background grey -text \\\n");
-  printf("\t\t\"$title\"  -relief raised -bg grey\n");
+  printf("\t\t\"%s\"  -relief raised -bg grey\n",label);
   printf("\tpack $w.m -pady 10 -side top -padx 10\n");
-  printf("\twm title $w \"$title\" \n\n\n");
+  printf("\twm title $w \"%s\" \n\n\n", label);
 }
 
 /*
@@ -79,6 +91,14 @@
     }
 
   /*
+   * Now write this option.
+   */
+  if( (item->flags & GLOBAL_WRITTEN) == 0)
+    {
+	printf("\tglobal %s\n", item->optionname);
+    }
+
+  /*
    * Now generate the body of the conditional.
    */
   printf("\tif {");
@@ -164,10 +184,12 @@
       printf(".menu%d.x%d.y configure -state normal;",menu_num, line_num);
       printf(".menu%d.x%d.n configure -state normal;",menu_num, line_num);
       printf(".menu%d.x%d.l configure -state normal;",menu_num, line_num);
+      printf("set %s [expr $%s&15];", item->optionname, item->optionname);
       printf("} else { ");
       printf(".menu%d.x%d.y configure -state disabled;",menu_num, line_num);
       printf(".menu%d.x%d.n configure -state disabled;",menu_num, line_num);
       printf(".menu%d.x%d.l configure -state disabled;",menu_num, line_num);
+      printf("set %s [expr $%s|16];", item->optionname, item->optionname);
       printf("}\n");
 #endif
       break;
@@ -190,11 +212,21 @@
       printf(".menu%d.x%d.n configure -state normal;",menu_num, line_num);
       printf(".menu%d.x%d.m configure -state normal;",menu_num, line_num);
       printf(".menu%d.x%d.l configure -state normal;",menu_num, line_num);
+      /*
+       * Or in a bit to the variable - this causes all of the radiobuttons
+       * to be deselected (i.e. not be red).
+       */
+      printf("set %s [expr $%s&15];", item->optionname, item->optionname);
       printf("} else { ");
       printf(".menu%d.x%d.y configure -state disabled;",menu_num, line_num);
       printf(".menu%d.x%d.n configure -state disabled;",menu_num, line_num);
       printf(".menu%d.x%d.m configure -state disabled;",menu_num, line_num);
       printf(".menu%d.x%d.l configure -state disabled;",menu_num, line_num);
+      /*
+       * Clear the disable bit - this causes the correct radiobutton
+       * to appear selected (i.e. turn red).
+       */
+      printf("set %s [expr $%s|16];", item->optionname, item->optionname);
       printf("}\n");
       break;
     default:
@@ -325,11 +357,19 @@
    */
   printf("\tbutton $w.f.prev -text \"Prev\" -activebackground green \\\n");
       printf("\t\t-width 15 -command \" destroy $w; focus $oldFocus; menu%d .menu%d \\\"$title\\\"\"\n", menu_num-1, menu_num-1);
+#ifdef PREVLAST_LIMITED_RANGE
   if(first == menu_num ) printf("\t$w.f.prev configure -state disabled\n");
+#else
+  if( 1 == menu_num ) printf("\t$w.f.prev configure -state disabled\n");
+#endif
 
   printf("\tbutton $w.f.next -text \"Next\" -activebackground green \\\n");
   printf("\t\t-width 15 -command \" destroy $w; focus $oldFocus;  menu%d .menu%d \\\"$title\\\"\"\n", menu_num+1, menu_num+1);
+#ifdef PREVLAST_LIMITED_RANGE
   if(last == menu_num ) printf("\t$w.f.next configure -state disabled\n");
+#else
+  if(last == tot_menu_num ) printf("\t$w.f.next configure -state disabled\n");
+#endif
 
   printf("\tbutton $w.f.back -text \"Main Menu\" -activebackground green \\\n");
   printf("\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n");
@@ -338,7 +378,9 @@
   printf("\tpack $w.f -pady 10 -side top -padx 10 -anchor w\n");
   printf("\tfocus $w\n");
   printf("\tupdate_menu%d $w\n", menu_num);
-  printf("\twm geometry $w +30+35\n");
+  printf("\tglobal winx; global winy\n");
+  printf("\tset winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]\n");
+  printf("\twm geometry $w +$winx+$winy\n");
   printf("}\n\n\n");
 
   /*
@@ -438,6 +480,7 @@
   int menu_line = 0;
   int menu_maxlines = 0;
   struct kconfig * cfg;
+  char * menulabel;
 
   /*
    * Start by assigning menu numbers, and submenu numbers.
@@ -486,6 +529,13 @@
     }
 
   /*
+   * Record this so we can set up the prev/next buttons correctly.
+   * We will be adding one more menu for sound configuration, so
+   * take this into account.
+   */
+  tot_menu_num = menu_num+1;
+
+  /*
    * Now start generating the actual wish script that we will use.
    * We need to keep track of the menu numbers of the min/max menu
    * for a range of submenus so that we can correctly limit the
@@ -509,6 +559,7 @@
 	    {
 	      end_proc(menu_num, menu_min, menu_max);
 	    }
+	  menulabel = cfg->label;
 	  start_proc(cfg->label, cfg->menu_number, TRUE);
 	  menu_num = cfg->menu_number;
 	  menu_max = cfg->submenu_end;
@@ -523,7 +574,7 @@
 	  if( cfg->menu_number != menu_num )
 	    {
 	      end_proc(menu_num, menu_min, menu_max);
-	      start_proc(cfg->label, cfg->menu_number, FALSE);
+	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
 	  printf("\tbool $w %d %d \"%s\" %s %s\n",
@@ -538,7 +589,7 @@
 	  if( cfg->menu_number != menu_num )
 	    {
 	      end_proc(menu_num, menu_min, menu_max);
-	      start_proc(cfg->label, cfg->menu_number, FALSE);
+	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
 	  printf("\ttristate $w %d %d \"%s\" %s %s\n",
@@ -552,7 +603,7 @@
 	  if( cfg->menu_number != menu_num )
 	    {
 	      end_proc(menu_num, menu_min, menu_max);
-	      start_proc(cfg->label, cfg->menu_number, FALSE);
+	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
 	  printf("\tdep_tristate $w %d %d \"%s\" %s %s\n",
@@ -564,6 +615,12 @@
 		 cfg->depend);
 	  break;
 	case tok_int:
+	  if( cfg->menu_number != menu_num )
+	    {
+	      end_proc(menu_num, menu_min, menu_max);
+	      start_proc(menulabel, cfg->menu_number, FALSE);
+	      menu_num = cfg->menu_number;
+	    }
 	  printf("\tint $w %d %d \"%s\" %s %s\n",
 		 cfg->menu_number,
 		 cfg->menu_line,
diff -u --recursive --new-file v1.3.35/linux/scripts/tkparse.c linux/scripts/tkparse.c
--- v1.3.35/linux/scripts/tkparse.c	Mon Oct 16 18:38:24 1995
+++ linux/scripts/tkparse.c	Sat Oct 21 12:36:39 1995
@@ -26,6 +26,7 @@
 struct kconfig * clast = NULL;
 struct kconfig * koption = NULL;
 static int lineno = 0;
+static int menus_seen = 0;
 /*
  * Simple function just to skip over spaces and tabs in config.in.
  */
@@ -243,6 +244,7 @@
     }
   else if (strncmp(pnt, "mainmenu_option", 15) == 0) 
     {
+      menus_seen++;
       tok = tok_menuoption;
       pnt += 15;
     }
@@ -441,6 +443,12 @@
     }
 
 
+  if( menus_seen == 0 )
+    {
+      fprintf(stderr,"The config.in file for this platform does not support\n");
+      fprintf(stderr,"menus.\n");
+      exit(1);
+    }
   /*
    * Input file is now parsed.  Next we need to go through and attach
    * the correct conditions to each of the actual menu items and kill
diff -u --recursive --new-file v1.3.35/linux/scripts/tkparse.h linux/scripts/tkparse.h
--- v1.3.35/linux/scripts/tkparse.h	Mon Oct 16 18:38:24 1995
+++ linux/scripts/tkparse.h	Sat Oct 21 12:36:38 1995
@@ -41,7 +41,9 @@
   union var variable;
 };
 
-#define GLOBAL_WRITTEN 1
+#define GLOBAL_WRITTEN  1
+#define CFG_DUP 	2
+
 struct kconfig
 {
   struct kconfig * next;
