]> git.neil.brown.name Git - history.git/commitdiff
Import 1.3.36 1.3.36
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:19 +0000 (15:10 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:19 +0000 (15:10 -0500)
219 files changed:
Documentation/cdrom/aztcd [new file with mode: 0644]
Documentation/cdrom/cdu31a [new file with mode: 0644]
Documentation/cdrom/cm206 [new file with mode: 0644]
Documentation/cdrom/gscd [new file with mode: 0644]
Documentation/cdrom/mcd [new file with mode: 0644]
Documentation/cdrom/mcdx [new file with mode: 0644]
Documentation/cdrom/optcd [new file with mode: 0644]
Documentation/cdrom/sbpcd [new file with mode: 0644]
Documentation/cdrom/sjcd [new file with mode: 0644]
Documentation/cdrom/sonycd535 [new file with mode: 0644]
MAGIC
Makefile
arch/alpha/config.in
arch/alpha/defconfig [new file with mode: 0644]
arch/i386/config.in
arch/i386/defconfig [new file with mode: 0644]
drivers/Makefile
drivers/block/Config.in [new file with mode: 0644]
drivers/block/Makefile
drivers/block/README.aztcd [deleted file]
drivers/block/README.cdu31a [deleted file]
drivers/block/README.cm206 [deleted file]
drivers/block/README.gscd [deleted file]
drivers/block/README.ide
drivers/block/README.mcd [deleted file]
drivers/block/README.mcdx [deleted file]
drivers/block/README.optcd [deleted file]
drivers/block/README.sbpcd [deleted file]
drivers/block/README.sjcd [deleted file]
drivers/block/README.sonycd535 [deleted file]
drivers/block/aztcd.c [deleted file]
drivers/block/blk.h [deleted file]
drivers/block/cdu31a.c [deleted file]
drivers/block/cm206.c [deleted file]
drivers/block/floppy.c
drivers/block/genhd.c
drivers/block/gscd.c [deleted file]
drivers/block/hd.c
drivers/block/ide.c
drivers/block/ide.h
drivers/block/ll_rw_blk.c
drivers/block/mcd.c [deleted file]
drivers/block/mcdx.c [deleted file]
drivers/block/optcd.c [deleted file]
drivers/block/ramdisk.c
drivers/block/sbpcd.c [deleted file]
drivers/block/sbpcd2.c [deleted file]
drivers/block/sbpcd3.c [deleted file]
drivers/block/sbpcd4.c [deleted file]
drivers/block/sjcd.c [deleted file]
drivers/block/sonycd535.c [deleted file]
drivers/block/xd.c
drivers/cdrom/Config.in [new file with mode: 0644]
drivers/cdrom/Makefile [new file with mode: 0644]
drivers/cdrom/aztcd.c [new file with mode: 0644]
drivers/cdrom/cdu31a.c [new file with mode: 0644]
drivers/cdrom/cm206.c [new file with mode: 0644]
drivers/cdrom/gscd.c [new file with mode: 0644]
drivers/cdrom/mcd.c [new file with mode: 0644]
drivers/cdrom/mcdx.c [new file with mode: 0644]
drivers/cdrom/optcd.c [new file with mode: 0644]
drivers/cdrom/sbpcd.c [new file with mode: 0644]
drivers/cdrom/sbpcd2.c [new file with mode: 0644]
drivers/cdrom/sbpcd3.c [new file with mode: 0644]
drivers/cdrom/sbpcd4.c [new file with mode: 0644]
drivers/cdrom/sjcd.c [new file with mode: 0644]
drivers/cdrom/sonycd535.c [new file with mode: 0644]
drivers/char/Config.in [new file with mode: 0644]
drivers/net/Config.in [new file with mode: 0644]
drivers/net/eql.c
drivers/net/ppp.c
drivers/pci/pci.c
drivers/scsi/53c7,8xx.c
drivers/scsi/Config.in [new file with mode: 0644]
drivers/scsi/NCR53c406a.c
drivers/scsi/aha152x.c
drivers/scsi/aha152x.h
drivers/scsi/aha1542.c
drivers/scsi/aha1740.c
drivers/scsi/aic7xxx.c
drivers/scsi/aic7xxx.h
drivers/scsi/aic7xxx.seq
drivers/scsi/aic7xxx_asm.c
drivers/scsi/aic7xxx_proc.c [new file with mode: 0644]
drivers/scsi/buslogic.c
drivers/scsi/constants.c
drivers/scsi/eata.c
drivers/scsi/eata_dma.c
drivers/scsi/eata_pio.h
drivers/scsi/fdomain.c
drivers/scsi/g_NCR5380.c
drivers/scsi/hosts.c
drivers/scsi/in2000.c
drivers/scsi/pas16.c
drivers/scsi/qlogic.c
drivers/scsi/scsi.c
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_ioctl.c
drivers/scsi/scsi_proc.c
drivers/scsi/scsi_syms.c
drivers/scsi/scsicam.c
drivers/scsi/sd.c
drivers/scsi/sd_ioctl.c
drivers/scsi/seagate.c
drivers/scsi/sg.c
drivers/scsi/sr.c
drivers/scsi/sr_ioctl.c
drivers/scsi/st.c
drivers/scsi/st.h
drivers/scsi/t128.c
drivers/scsi/u14-34f.c
drivers/scsi/ultrastor.c
drivers/scsi/wd7000.c
drivers/sound/.objects [new file with mode: 0644]
drivers/sound/.version
drivers/sound/CHANGELOG
drivers/sound/Config.in [new file with mode: 0644]
drivers/sound/Makefile
drivers/sound/Readme
drivers/sound/adlib_card.c
drivers/sound/audio.c
drivers/sound/configure.c
drivers/sound/dev_table.c
drivers/sound/dev_table.h
drivers/sound/dmabuf.c
drivers/sound/gus_wave.c
drivers/sound/hex2hex.h
drivers/sound/maui.c
drivers/sound/midi_synth.c
drivers/sound/midibuf.c
drivers/sound/mpu401.c
drivers/sound/opl3.c
drivers/sound/os.h
drivers/sound/patmgr.c
drivers/sound/pss.c
drivers/sound/sb16_dsp.c
drivers/sound/sb_dsp.c
drivers/sound/sequencer.c
drivers/sound/sound_calls.h
drivers/sound/sound_config.h
drivers/sound/sound_switch.c
drivers/sound/sound_timer.c
drivers/sound/soundcard.c
drivers/sound/sscape.c
drivers/sound/sys_timer.c
drivers/sound/trix.c
fs/Config.in [new file with mode: 0644]
fs/binfmt_elf.c
fs/buffer.c
fs/ext2/truncate.c
fs/msdos/misc.c
fs/nfs/rpcsock.c
fs/open.c
fs/proc/array.c
fs/read_write.c
include/asm-alpha/floppy.h [new file with mode: 0644]
include/asm-alpha/system.h
include/asm-i386/floppy.h [new file with mode: 0644]
include/asm-i386/smp.h
include/asm-mips/floppy.h [new file with mode: 0644]
include/linux/blk.h [new file with mode: 0644]
include/linux/cyclades.h
include/linux/firewall.h [new file with mode: 0644]
include/linux/fs.h
include/linux/if_ppp.h
include/linux/ip_fw.h
include/linux/mm.h
include/linux/module.h
include/linux/mroute.h
include/linux/sched.h
include/net/ip.h
include/net/sock.h
init/main.c
kernel/ksyms.c
kernel/sched.c
kernel/sys.c
mm/filemap.c
mm/memory.c
net/Changes
net/Config.in [new file with mode: 0644]
net/appletalk/aarp.c
net/appletalk/ddp.c
net/ax25/af_ax25.c
net/core/Makefile
net/core/datagram.c
net/core/dev.c
net/core/dev_mcast.c
net/core/firewall.c [new file with mode: 0644]
net/core/iovec.c
net/core/skbuff.c
net/core/sock.c
net/ipv4/Config.in [new file with mode: 0644]
net/ipv4/Makefile
net/ipv4/af_inet.c
net/ipv4/icmp.c
net/ipv4/ip.c [deleted file]
net/ipv4/ip_forward.c [new file with mode: 0644]
net/ipv4/ip_fragment.c [new file with mode: 0644]
net/ipv4/ip_fw.c
net/ipv4/ip_input.c [new file with mode: 0644]
net/ipv4/ip_options.c [new file with mode: 0644]
net/ipv4/ip_output.c [new file with mode: 0644]
net/ipv4/ip_sockglue.c [new file with mode: 0644]
net/ipv4/ipip.c
net/ipv4/packet.c
net/ipv4/raw.c
net/ipv4/tcp.c
net/ipv4/udp.c
net/ipx/af_ipx.c
net/netrom/af_netrom.c
net/netrom/nr_route.c
net/socket.c
scripts/Configure
scripts/header.tk
scripts/tail.tk
scripts/tkcond.c
scripts/tkgen.c
scripts/tkparse.c
scripts/tkparse.h

diff --git a/Documentation/cdrom/aztcd b/Documentation/cdrom/aztcd
new file mode 100644 (file)
index 0000000..6fa8ce5
--- /dev/null
@@ -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 --git a/Documentation/cdrom/cdu31a b/Documentation/cdrom/cdu31a
new file mode 100644 (file)
index 0000000..9900a32
--- /dev/null
@@ -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 --git a/Documentation/cdrom/cm206 b/Documentation/cdrom/cm206
new file mode 100644 (file)
index 0000000..9c4f928
--- /dev/null
@@ -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 --git a/Documentation/cdrom/gscd b/Documentation/cdrom/gscd
new file mode 100644 (file)
index 0000000..66d4baa
--- /dev/null
@@ -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 --git a/Documentation/cdrom/mcd b/Documentation/cdrom/mcd
new file mode 100644 (file)
index 0000000..39537f9
--- /dev/null
@@ -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 --git a/Documentation/cdrom/mcdx b/Documentation/cdrom/mcdx
new file mode 100644 (file)
index 0000000..28b5b66
--- /dev/null
@@ -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 --git a/Documentation/cdrom/optcd b/Documentation/cdrom/optcd
new file mode 100644 (file)
index 0000000..514c9d9
--- /dev/null
@@ -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 --git a/Documentation/cdrom/sbpcd b/Documentation/cdrom/sbpcd
new file mode 100644 (file)
index 0000000..f0ad67b
--- /dev/null
@@ -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 --git a/Documentation/cdrom/sjcd b/Documentation/cdrom/sjcd
new file mode 100644 (file)
index 0000000..f475e80
--- /dev/null
@@ -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 --git a/Documentation/cdrom/sonycd535 b/Documentation/cdrom/sonycd535
new file mode 100644 (file)
index 0000000..71d3a86
--- /dev/null
@@ -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 --git a/MAGIC b/MAGIC
index 999694d513b22571ed5d3bbc3215f71b46bcf57a..0b1a074b44ddd61fb505cf4e51d326b5e2b86444 100644 (file)
--- a/MAGIC
+++ b/MAGIC
@@ -36,39 +36,62 @@ major device number as the ioctl magic value (e.g. hd, lp).
                                        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
index b76e84b48b65e0d4cdc6f3ae2069bb9d0c316a67..19085b68c204058a6c2a985fd2fc96e3784fd2f5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 35
+SUBLEVEL = 36
 
 ARCH = i386
 
@@ -94,7 +94,11 @@ DRIVERS              =drivers/block/block.a \
 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
 
index 93b13e162ae0ce97f53b7f5306bd1af858f625d5..bfabf31bec6b0f0a6bd63f461a7ad501ca8a46c7 100644 (file)
@@ -13,23 +13,6 @@ else
   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 @@ fi
 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
-
-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
+tristate 'SCSI support' CONFIG_SCSI
 
-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
 
+source fs/Config.in
 
-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
-
-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 --git a/arch/alpha/defconfig b/arch/alpha/defconfig
new file mode 100644 (file)
index 0000000..db6edae
--- /dev/null
@@ -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
index 22dc4a867a17dd7a14aa61205d09ba3e9b8d34b1..5226710db3a4c6abcb02c2e3a66596fb0d6c15d2 100644 (file)
@@ -7,324 +7,78 @@ mainmenu_name "Linux Kernel Configuration"
 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 --git a/arch/i386/defconfig b/arch/i386/defconfig
new file mode 100644 (file)
index 0000000..140ab42
--- /dev/null
@@ -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
index dd8fba734419542d8ccbef24e2f3c0b3c9a628fc..a1fe2e76a2211f55eb06d51e3f221d98284d367b 100644 (file)
@@ -34,4 +34,9 @@ else
   endif
 endif
 
+ifdef CONFIG_CD_NO_IDESCSI
+SUB_DIRS += cdrom
+MOD_SUB_DIRS += cdrom
+endif
+
 include $(TOPDIR)/Rules.make
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
new file mode 100644 (file)
index 0000000..41eeaa9
--- /dev/null
@@ -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
index 0123f1aa1fc68a92829ddc360a0b907ca3406d73..0a8fcd90419710d92fac25b346e8aa3d4751ef3f 100644 (file)
@@ -28,98 +28,6 @@ else
   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 --git a/drivers/block/README.aztcd b/drivers/block/README.aztcd
deleted file mode 100644 (file)
index 6fa8ce5..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.cdu31a b/drivers/block/README.cdu31a
deleted file mode 100644 (file)
index 9900a32..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.cm206 b/drivers/block/README.cm206
deleted file mode 100644 (file)
index 9c4f928..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.gscd b/drivers/block/README.gscd
deleted file mode 100644 (file)
index 66d4baa..0000000
+++ /dev/null
@@ -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)
index b2e5e4e3158fbf81dd3612ad12647e35084e25a4..8250b1f3fe96c693f24993a0e54d8053992aee84 100644 (file)
@@ -36,6 +36,8 @@ NEW!  - support for reliable operation of buggy CMD-640 interfaces
                - 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 @@ NEW!    - ide-cd.c now supports door locking and auto-loading.
 
 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 @@ Summary of ide driver parameters for kernel "command line":
 
   "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 --git a/drivers/block/README.mcd b/drivers/block/README.mcd
deleted file mode 100644 (file)
index 39537f9..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.mcdx b/drivers/block/README.mcdx
deleted file mode 100644 (file)
index 28b5b66..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.optcd b/drivers/block/README.optcd
deleted file mode 100644 (file)
index 514c9d9..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.sbpcd b/drivers/block/README.sbpcd
deleted file mode 100644 (file)
index f0ad67b..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.sjcd b/drivers/block/README.sjcd
deleted file mode 100644 (file)
index f475e80..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/README.sonycd535 b/drivers/block/README.sonycd535
deleted file mode 100644 (file)
index 71d3a86..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/aztcd.c b/drivers/block/aztcd.c
deleted file mode 100644 (file)
index 99cd26d..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/blk.h b/drivers/block/blk.h
deleted file mode 100644 (file)
index 4778c2b..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/cdu31a.c b/drivers/block/cdu31a.c
deleted file mode 100644 (file)
index ba60e7f..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/cm206.c b/drivers/block/cm206.c
deleted file mode 100644 (file)
index 1697017..0000000
+++ /dev/null
@@ -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
index 1d126c8257191a36097171733a173a868aabad5c..7b3a4d1cb0d8a1669232ca0989fcc07f5579ddf4 100644 (file)
  * 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 @@ char kernel_version[] = UTS_RELEASE;
 int FLOPPY_IRQ=6;
 int FLOPPY_DMA=2;
 int ALLOWED_DRIVE_MASK = 0x33;
-int FDC1 = 0x3f0;
-int FDC2 = -1;
 
 #endif
 
@@ -128,8 +135,6 @@ static int ALLOWED_DRIVE_MASK=0x33;
 
 #define FLOPPY_IRQ 6
 #define FLOPPY_DMA 2
-#define FDC1 0x3f0
-static int FDC2=-1;
 #endif
 
 #define MODULE_AWARE_DRIVER
@@ -223,17 +228,19 @@ struct old_floppy_fdc_state {
 #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 @@ static unsigned long dma_mem_alloc(int size)
        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,23 +334,8 @@ static inline int DRIVE(kdev_t x) {
  */
 #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()'
  */
@@ -737,13 +713,13 @@ static int disk_change(int drive)
        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 @@ static int set_dor(int fdc, char mask, char data)
                        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 @@ static void twaddle(void)
 {
        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 @@ static void set_fdc(int drive)
        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 @@ static void setup_DMA(void)
        }
 #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 @@ static int output_byte(char byte)
        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 @@ static int result(void)
        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 @@ static int result(void)
                                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 @@ static int fdc_dtr(void)
                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 @@ static void reset_fdc(void)
        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 @@ void show_floppy(void)
        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 @@ static void floppy_shutdown(void)
        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 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
                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 @@ static int fd_copyout(void *param, volatile void *address, int size)
        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 @@ int floppy_init(void)
        initialising=0;
        if(have_no_fdc)
                unregister_blkdev(MAJOR_NR,"fd");
+       else
+               virtual_dma_init();
        return have_no_fdc;
 }
 
@@ -3939,28 +3923,27 @@ static int floppy_grab_irq_and_dma(void)
                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 @@ static void floppy_release_irq_and_dma(void)
 #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
index 5f4ea415b1b3f060fccac498f84df3062e8c59f6..35fd8b1100a656d7ac280a64004511eced661ee3 100644 (file)
@@ -213,7 +213,8 @@ check_table:
        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 --git a/drivers/block/gscd.c b/drivers/block/gscd.c
deleted file mode 100644 (file)
index 17bf8b9..0000000
+++ /dev/null
@@ -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
-
-
index ee62ad3667cc56d9576021f47e05a8481aaade69..a146f811ab304a01e47fad32476765b4eaef401a 100644 (file)
@@ -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);
 
index e336918e3fd144fd28fed72673001646c923072f..40aa8ac1c0a908083612c02364525140bb9e6feb 100644 (file)
@@ -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)
  */
  *                     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 byte   ide_hwif_to_major[MAX_HWIFS]   = {IDE0_MAJOR, IDE1_MAJOR, IDE2
 
 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 sub22 (char b, char c)
 
 static void init_dtc2278 (void)
 {
+       unsigned long flags;
+
+       save_flags(flags);
        cli();
 #if SET_DTC2278_MODE4
        /*
@@ -2375,11 +2383,39 @@ static void init_dtc2278 (void)
        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 @@ void init_cmd640_vlb (void)
        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 @@ static int match_parm (char *s, const char *keywords[], int vals[], int max_vals
  * "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 @@ void ide_setup (char *s)
                                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 @@ void ide_setup (char *s)
         * 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 @@ void ide_setup (char *s)
                        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 void probe_cmos_for_drives (ide_hwif_t *hwif)
 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 @@ static int init_irq (ide_hwif_t *hwif)
                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 ide_pci_access_error (int rc)
 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 @@ void init_cmd640 (byte bus, byte fn)
        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 @@ void init_cmd640 (byte bus, byte fn)
        } 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 @@ void init_cmd640 (byte bus, byte fn)
        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 @@ int ide_init (void)
                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;
index 00ed6073f0699981c480907842394ac5c7af2a6f..9f08dc98c7631faf82383949bced9b4640cd55a5 100644 (file)
@@ -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 @@ typedef struct hwgroup_s {
  * 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 *);
index 313f989ce400232872111e95039e194116ac43c0..d5554b7f9399124e1cddc41b64e2888542db8796 100644 (file)
@@ -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 --git a/drivers/block/mcd.c b/drivers/block/mcd.c
deleted file mode 100644 (file)
index fa997d5..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/mcdx.c b/drivers/block/mcdx.c
deleted file mode 100644 (file)
index 08bb838..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/optcd.c b/drivers/block/optcd.c
deleted file mode 100644 (file)
index 347e81d..0000000
+++ /dev/null
@@ -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);
-}
index bf98ecf0724779a42501b9b625d197b083058628..f4dbbd78a5543d026442c4be0549194623607e19 100644 (file)
@@ -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 --git a/drivers/block/sbpcd.c b/drivers/block/sbpcd.c
deleted file mode 100644 (file)
index 11a46ee..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/sbpcd2.c b/drivers/block/sbpcd2.c
deleted file mode 100644 (file)
index 82e2c68..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 2
-#include "sbpcd.c"
diff --git a/drivers/block/sbpcd3.c b/drivers/block/sbpcd3.c
deleted file mode 100644 (file)
index 0e79c41..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 3
-#include "sbpcd.c"
diff --git a/drivers/block/sbpcd4.c b/drivers/block/sbpcd4.c
deleted file mode 100644 (file)
index f4b34cc..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 4
-#include "sbpcd.c"
diff --git a/drivers/block/sjcd.c b/drivers/block/sjcd.c
deleted file mode 100644 (file)
index 93facba..0000000
+++ /dev/null
@@ -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 --git a/drivers/block/sonycd535.c b/drivers/block/sonycd535.c
deleted file mode 100644 (file)
index 0b5626f..0000000
+++ /dev/null
@@ -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 */
index f3afa1506a5620cb420ea4f487b2641369995087..b0c08297683e9e530c71b45e814c61a59d9b1764 100644 (file)
@@ -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 --git a/drivers/cdrom/Config.in b/drivers/cdrom/Config.in
new file mode 100644 (file)
index 0000000..2a87042
--- /dev/null
@@ -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 --git a/drivers/cdrom/Makefile b/drivers/cdrom/Makefile
new file mode 100644 (file)
index 0000000..13bbd50
--- /dev/null
@@ -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 --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c
new file mode 100644 (file)
index 0000000..03d614e
--- /dev/null
@@ -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 --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c
new file mode 100644 (file)
index 0000000..6fe1bda
--- /dev/null
@@ -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 --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c
new file mode 100644 (file)
index 0000000..9c7adad
--- /dev/null
@@ -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 --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c
new file mode 100644 (file)
index 0000000..d935705
--- /dev/null
@@ -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 --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c
new file mode 100644 (file)
index 0000000..424e2ff
--- /dev/null
@@ -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 --git a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c
new file mode 100644 (file)
index 0000000..4c20166
--- /dev/null
@@ -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 --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c
new file mode 100644 (file)
index 0000000..d19dc06
--- /dev/null
@@ -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 --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c
new file mode 100644 (file)
index 0000000..6011e9c
--- /dev/null
@@ -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 --git a/drivers/cdrom/sbpcd2.c b/drivers/cdrom/sbpcd2.c
new file mode 100644 (file)
index 0000000..82e2c68
--- /dev/null
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 2
+#include "sbpcd.c"
diff --git a/drivers/cdrom/sbpcd3.c b/drivers/cdrom/sbpcd3.c
new file mode 100644 (file)
index 0000000..0e79c41
--- /dev/null
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 3
+#include "sbpcd.c"
diff --git a/drivers/cdrom/sbpcd4.c b/drivers/cdrom/sbpcd4.c
new file mode 100644 (file)
index 0000000..f4b34cc
--- /dev/null
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 4
+#include "sbpcd.c"
diff --git a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c
new file mode 100644 (file)
index 0000000..c1f727e
--- /dev/null
@@ -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 --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c
new file mode 100644 (file)
index 0000000..b2b3101
--- /dev/null
@@ -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 --git a/drivers/char/Config.in b/drivers/char/Config.in
new file mode 100644 (file)
index 0000000..29756b4
--- /dev/null
@@ -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 --git a/drivers/net/Config.in b/drivers/net/Config.in
new file mode 100644 (file)
index 0000000..5109d7e
--- /dev/null
@@ -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
index 2f5eb57d6002e9e78b5c928146bfe2853cc3de30..bd4e89590c78b0859bc1bbce171f4658f789ac66 100644 (file)
@@ -854,26 +854,26 @@ slave_t *
 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 @@ int
 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 @@ eql_remove_slave_dev(slave_queue_t *queue, struct device *dev)
       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;
index cd20e0c018455fc7f29bd9db0e2a1cfca3360dff..2b304fdcff246ab153dd7c794b6c858262d7ce8a 100644 (file)
@@ -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 @@ ppp_init (struct device *dev)
        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>
                };
 
index 30ee8b1cf582056e511779211cafbe5ad106eb4a..f3132452c67dabaa1379ee27a557cde9ef8cf4d7 100644 (file)
@@ -59,7 +59,7 @@ struct pci_dev_info dev_info[] = {
        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 @@ struct pci_dev_info dev_info[] = {
        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")
index 725f6f2a69b01c97f62e55028a97812343e2d5f1..24495b0879a8d00626adf724da17f1eb91061520 100644 (file)
 #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 --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
new file mode 100644 (file)
index 0000000..6884205
--- /dev/null
@@ -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
index 8c435ec1f379dd0192eb4558c70c7e8c5c1797ba..c4233dcac79319ffea4a43c3ff94a5c87ffc974e 100644 (file)
@@ -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"
index 391aa8242894f428145c3541623d6e04908ae9ce..346abad738cc88cdd434834a3905997326bef957 100644 (file)
 
 #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"
index c56393de8b490a41fabaf97c14d223dc22568f6e..7a7890032e091cdbc0714d644c581bab0398e590 100644 (file)
@@ -7,7 +7,7 @@
 
 #if defined(__KERNEL__)
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include <asm/io.h>
 
index 16d41a91cc9eb57b7072e79bf66cdc53bb754905..5e8d826b3a8583f5197a2eda753324aef175b5a6 100644 (file)
@@ -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"
 
index f9b92d900986836dc4529c2a448561cb0ef9cb28..a92ec6fa81e43ce6b44d675c1abe0b42aaf7d4f7 100644 (file)
@@ -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"
index 1c9d953ef52568750d75f008dc628c9ea0d9f633..48108fbb60d2d487e2c136eb05db9b1010a20841 100644 (file)
@@ -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 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
     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 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
 
 /*
  * 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 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  *
  *   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 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  *     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 @@ typedef enum {
   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 @@ typedef enum {
  * 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).
@@ -266,6 +305,15 @@ typedef enum {
 #define                TID             0xF0            /* Target ID mask */
 #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
@@ -324,6 +372,16 @@ typedef enum {
 #define                ENPHASECHG      0x02
 #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
@@ -404,6 +462,7 @@ typedef enum {
 #define SEQADDR1(x)            ((x) + 0xC63ul)
 
 #define ACCUM(x)               ((x) + 0xC64ul)         /* accumulator */
+#define SINDEX(x)              ((x) + 0xC65ul)
 
 /*
  * Board Control (p. 3-43)
@@ -447,6 +506,13 @@ typedef enum {
 #define                UNPAUSE_284X    INTEN
 #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 @@ typedef enum {
 #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 @@ typedef enum {
 /*
  * 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 @@ typedef enum {
 #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 @@ typedef enum {
 #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 @@ struct seeprom_config {
  * 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 @@ static int aic7xxx_spurious_count;
 /*
  * 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 @@ struct aic7xxx_scb {
 /*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 @@ struct aic7xxx_scb {
 #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 @@ struct aic7xxx_host {
   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 @@ debug_config(struct aic7xxx_host_config *p)
    */
   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 @@ debug_config(struct aic7xxx_host_config *p)
         (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 @@ debug_config(struct aic7xxx_host_config *p)
          (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 @@ aic7xxx_delay(int seconds)
   }
 }
 
+#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
@@ -1263,6 +1488,73 @@ aic7xxx_info(struct Scsi_Host *notused)
   return buffer;
 }
 
+/*+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
@@ -1273,42 +1565,23 @@ aic7xxx_info(struct Scsi_Host *notused)
 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 @@ aic7xxx_getscb(int base, struct aic7xxx_scb *scb)
 
 /*+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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
   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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
     }
   }
 
-  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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
     }
 
     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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
 
       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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
         }
         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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
         */
        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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
        /*
         * 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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
          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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
          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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
         * 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_isr(int irq, struct pt_regs * regs)
          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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
                             :"si", "cx", "dx");
                outb(0, SCBCNT(base));
                outb(SCB_LIST_NULL, (SCBARRAY(base) + 30));
-
-               /*
-                * 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));
+                /*
+                 * 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));
+
+                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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
                 * 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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
        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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
            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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
        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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
           */
          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();
+                 channel, scsi_id, cmd->lun & 0x07);
 
-         /*
-          *  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->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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
        }
        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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
   {
     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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
        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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
 
        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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
       {
        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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
 
       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 @@ aic7xxx_isr(int irq, struct pt_regs * regs)
         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 @@ read_seeprom(int base, struct seeprom_config *sc)
   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 @@ read_seeprom(int base, struct seeprom_config *sc)
    */
   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 @@ read_seeprom(int base, struct seeprom_config *sc)
       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 @@ read_seeprom(int base, struct seeprom_config *sc)
       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 @@ read_seeprom(int base, struct seeprom_config *sc)
     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 @@ read_seeprom(int base, struct seeprom_config *sc)
   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 @@ detect_maxscb(aha_type type, int base)
       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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
   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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
        * 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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
       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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
       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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
                              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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
   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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
       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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
    */
   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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
 
   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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
   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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
      */
     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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
   {
     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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
       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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
        */
       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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
 #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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
   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 @@ aic7xxx_register(Scsi_Host_Template *template, aha_type type,
       /*
        * 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_detect(Scsi_Host_Template *template)
         */
        aic7xxx_spurious_count = 0;
 
-       index += 1;
+       index++;
       }
     }
   }
@@ -3414,8 +4094,8 @@ aic7xxx_buildscb(struct aic7xxx_host *p,
                 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 @@ aic7xxx_buildscb(struct aic7xxx_host *p,
       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 @@ aic7xxx_buildscb(struct aic7xxx_host *p,
   {
     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 @@ aic7xxx_buildscb(struct aic7xxx_host *p,
    * 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 @@ aic7xxx_buildscb(struct aic7xxx_host *p,
   {
 #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 @@ int
 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 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
       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 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
 
   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 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
    * 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 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
   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;
+
+    /*
+     * 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.
+     */
+    active_scb = inb(SCBPTR(base));
+    active_scbp = &(p->scb_array[active_scb]);
+    control = inb(SCBARRAY(base));
 
-  /*
-   * 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.
+     * Test to see if scbp is disconnected
      */
-    if (cmd->channel & 0x01)
+    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 @@ complete:
 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 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
 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*************************************************************************
@@ -4033,6 +4588,8 @@ aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
   return (0);
 }
 
+#include "aic7xxx_proc.c"
+
 #ifdef MODULE
 /* Eventually this will go into an include file, but this will be later */
 Scsi_Host_Template driver_template = AIC7XXX;
index c7929179032c08e68affeee72ca3dd81fb601e0a..4402719022692f5b5b7b154f35a9cbcb29ce1342 100644 (file)
  * 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,                                                   \
@@ -62,4 +62,6 @@ 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 */
index c4868426a6ff6920eeacfff31ddb583b0f024577..976dee66d5362e9f0dffef6a902ebeabb801314a 100644 (file)
@@ -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 @@ SELDO         = 0x40
 SELDI          = 0x20
 CLRSINT1       = 0x0c
 SSTAT1         = 0x0c
+PHASEMIS       = 0x10
 SIMODE1                = 0x11
 SCSIBUSL       = 0x12
 SHADDR         = 0x14
@@ -83,18 +84,19 @@ SCSICONF_A  = 0x5a
 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 @@ AWAITING_MSG        = 0xa1                          # Kernel requested to specify
                                                # (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 @@ AWAITING_MSG      = 0xa1                          # Kernel requested to specify
 #  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 @@ AWAITING_MSG        = 0xa1                          # Kernel requested to specify
 #  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 @@ SIGSTATE   = 0x4b                          # value written to SCSISIGO
 # 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 @@ SCBCOUNT    = 0x52                          # the actual number of SCBs
 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 @@ SAVED_TCL   = 0x56                          # Temporary storage for the
 # 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 @@ WAITING_SCBH      = 0x57                          # head of list of SCBs awaiting
                                                # 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 @@ test    SCBARRAY+0,NEEDDMA      jz test_busy
 # 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 @@ test    SCBARRAY+0,NEEDDMA      jz test_busy
 # 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 @@ start_waiting:
 
 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_scb:
 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 @@ start_selection:
 #  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 @@ mk_tag_done:
        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 @@ ITloop:
 
 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 @@ p_command:
        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 @@ p_status:
        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 @@ p_mesgin:
        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 @@ p_mesgin:
 #  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 @@ status_ok:
 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 @@ resid:
 #  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 @@ p_mesginWDTR:
        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.
+#  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.
 #
-p_mesgin4:
-       cmp     A,3             jne p_mesgin5   # restore pointers code?
-
-       call    sg_scb2ram
-       jmp     p_mesgin_done
+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 @@ setup_SCB:
        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 @@ abort_tag:
 #  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 @@ p_busfree:
 #  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 @@ inb_last1:
 dma:
        mov     DFCNTRL,SINDEX
 dma1:
-dma2:
        test    SSTAT0,0x1      jnz dma3        # DMADONE
        test    SSTAT1,0x10     jz dma1         # PHASEMIS, ie. underrun
 
@@ -978,19 +1007,14 @@ dma3:
 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 @@ initialize_for_target:
 
        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 @@ assert:
 
        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 @@ findSCB:
        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 @@ findSCB1:
        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 @@ ndx_dtr_2:
 #  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
 
index 4de3eaebaad35c2fdbbdbd69f065ed6cbd97477e..e227c22bdae19684d6704b1a3563e228727cf1fe 100644 (file)
@@ -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 --git a/drivers/scsi/aic7xxx_proc.c b/drivers/scsi/aic7xxx_proc.c
new file mode 100644 (file)
index 0000000..823e401
--- /dev/null
@@ -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:
+ */
index 4d06404e47d5a787b29ab3bb1bccf8121c8c7d3c..36027823f36d0d9843e5fc9865bca34fba4bbbe2 100644 (file)
@@ -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"
index 36701669301c4ff56fed300c9d97c245b78900a8..e35e8e046c7c40f17c3cab66e2c48504baaaabbe 100644 (file)
@@ -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"
index 1f380a337099e2b1f71e10b171708e755dfdfb50..290003324a225130ce6924b72f288cc9c8259127 100644 (file)
 #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"
index aede48fd6c37804f96ff900734531ea3a90de739..b0a94afb2769b7941120be697b86739b68c66ae8 100644 (file)
@@ -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"
index 4ad0402f8a208685a8f6978b4e61e6706035c5c7..8a626e0be548c800124696a984986350ed95c7f0 100644 (file)
@@ -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>
index 3cd9902064c5331372c2a647c7dfb98d78873df6..8488b936b315e62323a0f61c9716e5269a557444 100644 (file)
 
 #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"
index 012135c782a594bdf02c338893d9574204b23180..b780a7497bd39c3d4a7ab7535c070242a3150a13 100644 (file)
@@ -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"
index 14c7589e499513a8bd47b02ea61254115bff8bef..6dc67d0a6194cfaed6c11db77320e9d1c49d22e2 100644 (file)
@@ -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>
index b6726ef90bda45bca15fb92f81e913253f62bdbe..ac2cc6564f4a75931f76edb6667ca0d98df2a50c 100644 (file)
@@ -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"
index 6810366f64e7289c3ef38758af592e9349cf86f1..c004b61b5a110ce6868c6a73f2adca5d6938b39b 100644 (file)
 #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"
index b6f762d4e39f3a109ee1c25835580616a5903331..a0febf31748f890ece006e68e8b5e3f0e437bfe7 100644 (file)
 #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>
index 70f6d23d7a7a9c7000ee06288d3fbdce8773c186..1cbd2ba6b77720ef167fef4027f048da7d9b354a 100644 (file)
@@ -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"
index e0dcbd1d473773ba0c9b129e46c86268354e0be1..90374a769dba53c7e1957bb7775cbead0bf04fa3 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/module.h>
 #endif
 
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
 
index f7d0d2c3e16b5ba85da14bde678325d01802cbad..bbeaf9f74a2cf13b1a583bffca1b34ddd99b0694 100644 (file)
@@ -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"
index b84385cabbd60baf5ca74ddf0099e64135934c4d..ebd2195814a2e49d441d6183c60ceafea2dee72d 100644 (file)
@@ -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"
 
index c58ee79aeae78e90998129c03c01de294494dd98..1ad0ceea574b87bbaed261349a6ba942c364a062 100644 (file)
@@ -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"
index 97edb4804a21378ae7ea1e4a5ac13374275e8cdf..4ad51197895f00cda479956dd4695baf61b2a24e 100644 (file)
@@ -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"
index 7003aa5de4aee11d49de5dddf69ad6e579bc5ee6..d85f8b2da832b0f863b2a58bf7b276d11683536a 100644 (file)
@@ -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"
index 0df5d01bb8502d5a1971ab2699c8aa98d9ab8cf7..786c1c7292420f7a7df4e51de87edf4741834bbb 100644 (file)
@@ -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"
index befa51f5076ad31adafc8681be261512ea58094c..10337adbfc398e5380bcc2ff4dbf09e5aab53768 100644 (file)
@@ -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"
index 34b7c3b229a7ddb49ade9ed54316eefdd512936f..71b87ec098e3e5bbf6e68ab5f54a846e75f4f915 100644 (file)
@@ -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"
index 3992f44ad0a36c6ae484a81064ea19243e2c3ffd..fb5b7175fe88d932f0fd44d0db17f12ce1364fb7 100644 (file)
@@ -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"
index 1d7a325b65417414e10e91c2ce4dd879b7b1d676..9a10f4e83bbc2988f403d3a3d39fadd3292e05d0 100644 (file)
@@ -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"
index 3821a80935354c2a1e4368e903ca24282ec32ab3..247a528c408db70c78180dae3e4950589473f66d 100644 (file)
@@ -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 @@ st_chk_result(Scsi_Cmnd * SCpnt)
           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 @@ st_chk_result(Scsi_Cmnd * SCpnt)
        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 @@ st_sleep_done (Scsi_Cmnd * SCpnt)
     }
     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_sleep_done (Scsi_Cmnd * SCpnt)
 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 @@ st_do_scsi(Scsi_Cmnd *SCpnt, Scsi_Tape *STp, unsigned char *cmd, int bytes,
     }
 
   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 @@ st_do_scsi(Scsi_Cmnd *SCpnt, Scsi_Tape *STp, unsigned char *cmd, int bytes,
 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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
     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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
 #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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
        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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
             (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 @@ st_write(struct inode * inode, struct file * filp, const char * buf, int count)
       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,
index eea21e89a8f67e3c2ee17da9a7552781fdd9e4bc..476e543b94e514894b6e8797e07b078e1ac7d9bd 100644 (file)
@@ -29,7 +29,6 @@ typedef struct {
   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 @@ typedef struct {
   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 @@ typedef struct {
   struct mtget * mt_status;
   Scsi_Cmnd SCpnt;
 #if DEBUG
+  unsigned char write_pending;
   int nbr_finished;
   int nbr_waits;
 #endif
index 0b8fefa4c6540c5deedd6c5299cc38020faf0cde..0e89d1edba6718a00fa1dec88267b62802655b1e 100644 (file)
 #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"
index 62a6e46447a148554ff43d06f6ddc7c96e7b53c7..ce0e378ee1dea0d78acee1fa8a3487c74d111866 100644 (file)
 #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"
index 251861289e06413b84ba150a7d813da265945de6..23e94a91b39c439cfca1df25c9fa7cd4cd7856e9 100644 (file)
 #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"
index 1f77e4f26823ff525aa58f2fa2f23dd6c3688154..e2d8771f24e211efbc525f92ca2d115ac143acf9 100644 (file)
 #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 --git a/drivers/sound/.objects b/drivers/sound/.objects
new file mode 100644 (file)
index 0000000..92daeca
--- /dev/null
@@ -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
index 8d484ede63721ac6bf37717fb7c8c0e73cc15074..8e93d1ad4a2b8c926d95abca821f95c07edcd6e9 100644 (file)
@@ -1 +1 @@
-3.5-alpha2
+3.5-alpha3
index a4049d80c7bb129c31778018ca7846253ab7c5d4..f9c32a7ac7e69081f9bc5f9bab378432adcffbc1 100644 (file)
@@ -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 --git a/drivers/sound/Config.in b/drivers/sound/Config.in
new file mode 100644 (file)
index 0000000..305280d
--- /dev/null
@@ -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 ;;
+
index a993cc1b1fc39c569ffc6e2662d424c7a6f5e71c..bbe3d9ce06070269003ba32f8a0e983bea4b3ba9 100644 (file)
@@ -9,14 +9,20 @@ VERSION               = `cat .version`
 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 @@ install:     sound.o
        $(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 @@ indent:
 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 @@ config: configure
 #      @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 @@ dep:
 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
index 6c41943ed84b015368fdd39b34e8fc5b99c594d5..f308441eeeadbb06fed5fc44501fdafe42a932d2 100644 (file)
@@ -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 @@ IMPORTANT! This version of the driver is compatible only with Linux versions
           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 @@ sites). It contains much more information than this file.
 * 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 @@ them are still on my mailbox and they should be included in versions
 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 @@ MAD16 (an interface chip by OPTi) based soundcards (TB Tropez ???).
 (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.
index 3542cdca4223bd5e79e921ad2cde99d1b6cd8f7d..21f70d22b59c99e52ea963737cbbb3fda2a449b5 100644 (file)
@@ -35,11 +35,9 @@ long
 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;
 }
 
index 8b7ce7ce350dac4d558ebf5a52a22524072e15f9..d6f4be640160d9f1fd72457a32fd27377629967e 100644 (file)
@@ -154,20 +154,6 @@ audio_release (int dev, struct fileinfo *file)
   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 @@ translate_bytes (const void *table, void *buff, int n)
     }
 }
 
-#endif
 
 int
 audio_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int count)
@@ -247,7 +232,7 @@ audio_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int count)
        {                       /*
                                 * 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 @@ audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
          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 @@ audio_ioctl (int dev, struct fileinfo *file,
          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 @@ audio_ioctl (int dev, struct fileinfo *file,
          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 @@ audio_ioctl (int dev, struct fileinfo *file,
          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;
index c175b228178ace8a25d08ecb512df0379b46b2bd..af91f4ffff876026580aace526a26a9ce0591945 100644 (file)
@@ -1,4 +1,4 @@
-#define DISABLED_OPTIONS       0
+#define DISABLED_OPTIONS       (B(OPT_PNP))
 /*
  * sound/configure.c  - Configuration program for the Linux Sound Driver
  *
 #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)| \
 #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 @@ hw_entry        hw_table[] =
   {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 @@ char           *questions[] =
   "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 @@ char           *questions[] =
   "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 @@ play_it_again_Sam:
   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 @@ main (int argc, char *argv[])
   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 @@ main (int argc, char *argv[])
                        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 @@ main (int argc, char *argv[])
                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 @@ main (int argc, char *argv[])
          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 @@ main (int argc, char *argv[])
   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 @@ main (int argc, char *argv[])
        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 @@ main (int argc, char *argv[])
       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 @@ main (int argc, char *argv[])
   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);
 }
 
index 1ac12580252f93ce30e9f4c2fd236f98d3822f18..418d85dc6f251c023a2e62c5a12e58668a6cdf92 100644 (file)
@@ -72,13 +72,10 @@ sndtable_init (long mem_start)
                                                 */
        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 @@ sndtable_init (long mem_start)
                      snd_installed_cards[i].config.dma2);
            else
              printk ("\n");
-#endif
          }
        else
          snd_installed_cards[i].enabled = 0;   /*
index e802433b3b5c924974892a857bcdf03c4e367b0e..e7d75d1fce12e6ff6a0481fef22345fec09b18b4 100644 (file)
@@ -280,12 +280,8 @@ struct sound_timer_operations {
        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 sound_timer_operations {
 
        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
index 9ec3c0f6eeb0fcebd73f53cf0a0c2a6ddec301f1..8a88fb917db782a7444efa82014afcdfc5821b3c 100644 (file)
@@ -864,7 +864,7 @@ DMAbuf_ioctl (int dev, unsigned int cmd, ioctl_arg arg, int local)
        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 @@ DMAbuf_ioctl (int dev, unsigned int cmd, ioctl_arg arg, int local)
        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 */
index c25b8ab3801aac3926a350592f7c835e3761afb0..594520d1ba90964275448091d3262501b1e60655 100644 (file)
@@ -896,7 +896,7 @@ guswave_ioctl (int dev,
     {
     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 @@ compute_volume (int voice, int volume)
 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 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
 
   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 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
     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 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
       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 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
       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 @@ guswave_start_note2 (int dev, int voice, int note_num, int volume)
       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 @@ guswave_start_note (int dev, int voice, int note_num, int volume)
   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 @@ guswave_load_patch (int dev, int format, const snd_rw_buf * addr,
    * 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 @@ guswave_load_patch (int dev, int format, const snd_rw_buf * addr,
           * 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 @@ gus_copy_from_user (int dev, char *localbuf, int localoffs,
 {
   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 @@ gus_wave_init (long mem_start, struct address_info *hw_config)
     }
 
 
-  {
-    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 ();
 
index ecd7b4c4239c635bd638e4732d1d00df2e8336d5..5c909174780d423182057a04b99d53eecf0b92db 100644 (file)
@@ -81,7 +81,7 @@ int hex2hex(char *source, char *target, char *varline)
        }
 
        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++)
        {
index eff6ae211b8bd18512aad823a78f8a12ba4e3684..debb1d424e7fc92e2a9570a536671abed7cdeb7e 100644 (file)
@@ -85,7 +85,7 @@ maui_write (unsigned char data)
        }
     }
 
-  printk ("Maui: Transmit timeout\n");
+  printk ("Maui: Write timeout\n");
 
   return 0;
 }
@@ -128,7 +128,7 @@ maui_load_patch (int dev, int format, const snd_rw_buf * addr,
    * 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)
     {
index 3f1a6e4714a6a7e86cca15afa66ac4843108f1d5..33fa2bb965c5ab5b06890d6c725cdfa0add73d2e 100644 (file)
@@ -284,7 +284,7 @@ midi_synth_ioctl (int dev,
     {
 
     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 @@ midi_synth_load_patch (int dev, int format, const snd_rw_buf * addr,
    * 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)
     {
index ae582117d8c7f73f21794df6fe195600370435dd..ca1d7173bbc4a710ebd7bb5bcc6fca554ea4e833 100644 (file)
@@ -246,14 +246,7 @@ MIDIbuf_open (int dev, struct fileinfo *file)
     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 @@ MIDIbuf_open (int dev, struct fileinfo *file)
     }
   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 @@ MIDIbuf_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int count
 
       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 @@ MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
       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++;
        }
     }
index a34c8d9f96959cf575dd506d1ddbf868cdb3558c..76e4d2521cc0c3a8c5bbf9f09633e69956f61126 100644 (file)
@@ -770,7 +770,7 @@ mpu401_ioctl (int dev, unsigned cmd, ioctl_arg arg)
   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 @@ mpu401_ioctl (int dev, unsigned cmd, ioctl_arg arg)
        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 @@ mpu_synth_ioctl (int dev,
     {
 
     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 @@ static struct synth_operations mpu401_synth_proto =
   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 @@ attach_mpu401 (long mem_start, struct address_info *hw_config)
       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 @@ attach_mpu401 (long mem_start, struct address_info *hw_config)
          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 @@ attach_mpu401 (long mem_start, struct address_info *hw_config)
   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 @@ probe_mpu401 (struct address_info *hw_config)
     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 @@ mpu_timer_interrupt (void)
   if (curr_ticks >= next_event_time)
     {
       next_event_time = 0xffffffff;
-      sequencer_timer ();
+      sequencer_timer (0);
     }
 }
 
index e36b856bbab7a62a765f03c6c9113e95cc290fdc..789266825d1f72935077b85d37d3748a6e567095 100644 (file)
@@ -130,7 +130,7 @@ opl3_ioctl (int dev,
       {
        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 @@ opl3_ioctl (int dev,
     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 @@ opl3_detect (int ioaddr, sound_os_info * osp)
   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 @@ opl3_load_patch (int dev, int format, const snd_rw_buf * addr,
       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 @@ opl3_init (long mem_start, int ioaddr, sound_os_info * osp)
       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;
     }
 
index d565114f3cfe3ce6b0c0faf4a73a3294da72ba4c..aad18815c041e0c78a3b58f2aaab6d98de8e60dd 100644 (file)
@@ -1,6 +1,8 @@
 
 #define ALLOW_SELECT
 #define ALLOW_BUFFER_MAPPING
+#undef NO_INLINE_ASM
+#undef SHORT_BANNERS
 
 #ifdef MODULE
 #include <linux/config.h>
index 2296ce618253260e4e47b8120e4ecf5671f24aff..50090dfc32cc2ebe77ab1610a2365682caf5f701 100644 (file)
@@ -136,7 +136,7 @@ pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
 
       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 @@ pmgr_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int count)
       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 @@ pmgr_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int count)
 
   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 @@ pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
   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;
index 23368c99c2f1d3f3d9ebe195f9d3606a0d64172a..384bc4b07943d9f0762785f6515c3856d9d0edad 100644 (file)
@@ -510,17 +510,11 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
        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 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
        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 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
        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 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
        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 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
        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 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
        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 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
 
        restore_flags (flags);
 
-       memcpy_tofs (&(((char *) arg)[0]), (&buf), (sizeof (buf)));
+       memcpy_tofs ((&((char *) arg)[0]), &buf, sizeof (buf));
        return 0;
       }
       break;
index a5bdd78e75e2abb512967e637570874c3238588e..13fb3b39bc9883d46f613abad6e0d25068aab921 100644 (file)
@@ -49,6 +49,7 @@ static int      dsp_stereo = 0;
 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 @@ sb16_dsp_open (int dev, int mode)
 
   irq_mode = IMODE_NONE;
   dsp_busy = 1;
+  trigger_bits = irq_mode;
 
   return 0;
 }
@@ -398,6 +400,11 @@ sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
 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 @@ sb16_dsp_interrupt (int unused)
     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;
 
index 9d32a17d1e85f58d9ed84544c36638b41beff2b3..c6ab4647346058339cae1636a9d285bb6c8d2884 100644 (file)
@@ -1171,8 +1171,8 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
                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)
index 71a483c25f48b66ed49fb616c181c9786823607d..e3d38f4a37ba2dbb4735ca5742b54ec746471bd9 100644 (file)
@@ -156,7 +156,7 @@ sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
   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 @@ sequencer_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int cou
 
   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 @@ sequencer_write (int dev, struct fileinfo *file, const snd_rw_buf * buf, int cou
              return count - c;
            }
 
-         memcpy_fromfs ((&event[4]), &((buf)[p + 4]), (4));
+         memcpy_fromfs (&event[4], &((buf)[p + 4]), 4);
 
        }
       else
@@ -1763,7 +1763,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
            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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
            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 @@ sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * w
 
 
 void
-sequencer_timer (void)
+sequencer_timer (unsigned long dummy)
 {
   seq_startplay ();
 }
@@ -2087,23 +2079,13 @@ sequencer_init (long mem_start)
 
   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;
-  };
+  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++;;
 
-  {
-    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;
-  };
+  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;
 }
index c66ba51758ffbc348e1e0c74daa40a53c30d8a99..b92692c70a8964add4ae91be5ba563781fae6a84 100644 (file)
@@ -46,7 +46,7 @@ int sequencer_ioctl (int dev, struct fileinfo *file,
           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);
index 80dea8b62794f9ee01f51c7b7f7becd0ec8f6667..859a9f2561e00a4e72de155e363ab97dc5bc60fe 100644 (file)
 #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
index f954297dd9ff46bacd442651b87e5339c5fdf677..355d4c534f312935b964277ea2057a7a921164b2 100644 (file)
@@ -314,7 +314,7 @@ read_status (snd_rw_buf * buf, int count)
   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 @@ sound_open_sw (int dev, struct fileinfo *file)
       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 ();
index be38a360ddf93e7b23f1be9761757b4dc008cc90..fc387a9a3c5213a597f2baa6f0deed8a878bd659 100644 (file)
@@ -326,7 +326,7 @@ sound_timer_interrupt (void)
   if (curr_ticks >= next_event_time)
     {
       next_event_time = 0xffffffff;
-      sequencer_timer ();
+      sequencer_timer (0);
     }
 }
 
index 9e50c55df34456a6d8c0871174eed1e8be0fa928..ce7f14eb78572fadf1d9ff62db53af6073fb64f4 100644 (file)
 
 #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 @@ soundcard_init (void)
 {
 #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 @@ init_module (void)
       return err;
     }
 
+  chrdev_registered = 1;
   soundcard_init ();
 
   if (sound_num_blocks >= 1024)
@@ -450,7 +445,8 @@ cleanup_module (void)
     {
       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 @@ cleanup_module (void)
            sound_free_dma (i);
          }
 
-#ifndef EXCLUDE_PNP
-      sound_pnp_disconnect ();
-#endif
-
     }
 }
 #endif
@@ -571,6 +563,11 @@ sound_close_dma (int chn)
 }
 
 #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 @@ request_sound_timer (int count)
     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 @@ request_sound_timer (int count)
 void
 sound_stop_timer (void)
 {
-  timer_table[SOUND_TIMER].expires = 0;
-  timer_active &= ~(1 << SOUND_TIMER);
+  del_timer (&seq_timer);;
 }
 
 #ifndef EXCLUDE_AUDIO
index d5e8e220c7ca6084ae35b84e8490e56f1076d7d7..116d8a993fc6f54e0def135c59728c11746149dd 100644 (file)
@@ -693,16 +693,10 @@ sscape_coproc_ioctl (void *dev_info, unsigned int cmd, ioctl_arg arg, int local)
        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;
index fa3a386780baa724101e37ae039f30a2c735ca08..e251ee25cf8213d362d130c91afd1b56ef06254f 100644 (file)
@@ -80,7 +80,7 @@ poll_def_tmr (unsigned long dummy)
          if (curr_ticks >= next_event_time)
            {
              next_event_time = 0xffffffff;
-             sequencer_timer ();
+             sequencer_timer (0);
            }
        }
     }
index 700d1ea036fa0885b50b73b5b182fe818f8beb90..3c12d0e1df0de09c4d81f7b8a6a1df85243a120e 100644 (file)
@@ -353,13 +353,22 @@ probe_trix_mpu (struct address_info *hw_config)
   {-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 @@ probe_trix_mpu (struct address_info *hw_config)
     }
 
   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 --git a/fs/Config.in b/fs/Config.in
new file mode 100644 (file)
index 0000000..df6af9d
--- /dev/null
@@ -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
index 23c1f82f2c4af2872c1ff5ce72c640891ad0a0a0..6baf075ccc32d54886614eb28ac11febdbfb9444 100644 (file)
@@ -621,7 +621,11 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                        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;
index ed757e66002756b30de65b65a4abdd1c5e579936..0dde5edfc27cddc999384fab81d81cb253445ae9 100644 (file)
@@ -769,10 +769,14 @@ void set_writetime(struct buffer_head * buf, int flag)
 }
 
 
-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 @@ void refile_buffer(struct buffer_head * buf){
        }
 }
 
+/*
+ * Release a buffer head
+ */
 void brelse(struct buffer_head * buf)
 {
        if (!buf)
@@ -811,25 +818,37 @@ void brelse(struct buffer_head * buf)
        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);
+}
+
 /*
  * bread() reads a specified block and returns the buffer that contains
  * it. It returns NULL if the block was unreadable.
index 4bfcae3009cb46dfede42691a5b39b141e6a2996..bb8751d84bbe533eb998a5fd6cc271a3698072fc 100644 (file)
 #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 @@ repeat:
                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 @@ repeat:
                *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 @@ repeat:
                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 @@ repeat:
                }
                *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++;
index 0fc2ea9e6cec1fe34689458dadee49f866a2098a..30e88c17d90150ed1862c0f9af487ee7053c7b31 100644 (file)
@@ -120,7 +120,7 @@ void unlock_fat(struct super_block *sb)
 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 @@ printk("set to %x\n",fat_access(inode->i_sb,nr,-1));
           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 @@ static int raw_scan(struct super_block *sb,int start,const char *name,int *numbe
 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;
index e5de461fcac065c2dc8303f57d5f04758fefafbb..08c57e39bfdb54659b49e6fa6b00ce2dfb1a4a88 100644 (file)
@@ -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 @@ rpc_call_one(struct rpc_sock *rsock, struct rpc_wait *slot,
        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 @@ timedout:
                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 @@ rpc_makesock(struct file *file)
        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 @@ rpc_makesock(struct file *file)
                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 @@ rpc_closesock(struct rpc_sock *rsock)
        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;
index 97fb27743fe7c202910c3a128f1e512cb6b1d6f4..d56d5f9ef025ea0b8911953acaaf04ed4eae2e8e 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -61,11 +61,25 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf)
        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 @@ asmlinkage int sys_truncate(const char * path, unsigned int length)
                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 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned int length)
                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,
index f0014ce7aa83f25cf96a0b75a86367b7f6e740a7..45544115feb5419c8dc5e2e3938d90db4d151512 100644 (file)
@@ -405,6 +405,7 @@ static int get_stat(int pid, char * buffer)
        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 @@ static int get_stat(int pid, char * buffer)
                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 @@ static int get_stat(int pid, char * buffer)
                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,
index 75a713a7034e175cc5ea44435a921842cb046ad5..67bdf409151fe0c772b99a5ddc6ef320892e37a5 100644 (file)
@@ -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 @@ asmlinkage int sys_write(unsigned int fd,char * buf,unsigned int count)
        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 @@ asmlinkage int sys_readv(unsigned long fd, const struct iovec * vector, long cou
        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 --git a/include/asm-alpha/floppy.h b/include/asm-alpha/floppy.h
new file mode 100644 (file)
index 0000000..0642870
--- /dev/null
@@ -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 */
index c053add9d2d31bbdfb7ec59d9d3205fdc2c458e0..352e21f565d478168778befbda7478c98fa85302 100644 (file)
@@ -55,7 +55,7 @@ extern void wrmces (unsigned long);
 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 --git a/include/asm-i386/floppy.h b/include/asm-i386/floppy.h
new file mode 100644 (file)
index 0000000..844bbce
--- /dev/null
@@ -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 */
index f107729bba7c635909e5f1e1172f6a90bf28f880..f0ff77f51105c91b61b7cfd145a3d36345d11086 100644 (file)
@@ -228,5 +228,18 @@ extern __inline int smp_processor_id(void)
 #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 --git a/include/asm-mips/floppy.h b/include/asm-mips/floppy.h
new file mode 100644 (file)
index 0000000..32d72de
--- /dev/null
@@ -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 --git a/include/linux/blk.h b/include/linux/blk.h
new file mode 100644 (file)
index 0000000..4778c2b
--- /dev/null
@@ -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 */
index 182a8949a014260599a771e840d6e79260f33a1d..2d12b8dc3c212de286dfca659e2ef8f8e985e91b 100644 (file)
@@ -47,6 +47,9 @@ struct cyclades_monitor {
  * 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 --git a/include/linux/firewall.h b/include/linux/firewall.h
new file mode 100644 (file)
index 0000000..29493a7
--- /dev/null
@@ -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
index fdd73f7d83f76883eb841a3dd896a2e0d35149b8..57af1a48f8a2cebd8c7b97532f455af48d9132dd 100644 (file)
 #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_page(int rw, kdev_t dev, unsigned long nr, char * buffer);
 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);
index d308ecd772d8aaa9f35f32c2318bb5903642616b..2823b3f607cd3123d207ef79a461831995f420d1 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 3==
+ *  ==FILEVERSION 4==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
 #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
  */
index a8169db53e60bdc2f5d556f9e514cd887dc399fd..c96d34f5898663e0c979f0ed7d35ba4da3fbbbf7 100644 (file)
@@ -164,10 +164,6 @@ extern void ip_acct_cnt(struct iphdr *, struct device *, struct ip_fw *);
 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);
index 7b058ba86fbba3e4be446310bb7eeb4ed8f51381..0efcc47d3dc8c55615dc02f186b75c8185cf14a6 100644 (file)
@@ -186,6 +186,7 @@ extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t p
 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);
index e8c0fd0918b52f8aa20da470577c05735382a832..7791edd43bc1d4eb4e1047dafdd07843b351a9d0 100644 (file)
@@ -90,12 +90,12 @@ extern int register_symtab(struct symbol_table *);
  * 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)
index 8943510c619a2a90c76b305f158c9abedd7a080f..38aa312c36326355ae4bdd35e31bddd8d8be84d7 100644 (file)
@@ -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.
index 743a9d39b79744588e04d58744afbd02fd11539c..3f2c1fe4a6f12d37a3de160542e9d1dbdd260e3a 100644 (file)
@@ -222,7 +222,8 @@ struct task_struct {
        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 @@ struct task_struct {
  */
 #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 @@ extern struct task_struct *current_set[NR_CPUS];
  *     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;
index 1ed2e2939a6cb120ca8f0b9f8ddabba71215d5ad..f37c25eadc4e0f910e5e4f4fbe013e765b749050 100644 (file)
@@ -48,46 +48,51 @@ extern int          ip_mc_procinfo(char *, char **, off_t, int, int);
  
 
 /* 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 int           ip_id_count;
 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 int         ip_build_xmit(struct sock *sk,
 
 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 */
index 158311ef98523235c43484416692c040add71913..febac5c797e0c0d7029ef99433c791ec7b86c84f 100644 (file)
@@ -77,26 +77,27 @@ struct unix_opt
  * 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,201 +113,225 @@ struct sock {
                                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
+       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 */
+       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;
+       nr_cb                   *nr;
 #endif
 #endif
-#ifdef CONFIG_ATALK
-  struct atalk_sock            at;
-#endif
   
 /*
  *     This is where all the private (optional) areas that don't
  *     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 
+  */
+  
+       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);
+  /*
+   *   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 @@ extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        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 reset_timer (struct sock *, int, unsigned long);
 extern void net_timer (unsigned long);
 
 
-/* Enable debug/info messages */
+/* 
+ *     Enable debug/info messages 
+ */
 
 #define NETDEBUG(x)            x
 
index c87a397ad85f05251e381a6f8d6f37872b4ba45a..f1cbaab5073b2772ec499111775bdad40e458380 100644 (file)
@@ -460,9 +460,19 @@ static void smp_init(void)
                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
 
@@ -588,6 +598,15 @@ static int init(void * unused)
 
        setup();
 
+#ifdef CONFIG_SMP      
+       /*
+        *      With the devices probed and setup we can
+        *      now enter SMP mode.
+        */
+       
+       smp_begin();
+#endif 
+
        #ifdef CONFIG_UMSDOS_FS
        {
                /*
index addac29614dd9231379d7030bd1a76a5038778bb..11177af72f5ebdaaecb4b544bfdcbae4aa829382 100644 (file)
 #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 @@ struct symbol_table symbol_table = {
        /* 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 @@ struct symbol_table symbol_table = {
        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),
index f71cb35d8fdf9e7977e03e65404323c345b9c22d..3feb16cd2c5d05f29e5c9117ea0709a018e416fa 100644 (file)
@@ -166,6 +166,54 @@ static void process_timeout(unsigned long __data)
        wake_up_process(p);
 }
 
+/*
+ * 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 @@ asmlinkage void schedule(void)
        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 @@ asmlinkage void schedule(void)
        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 @@ asmlinkage void schedule(void)
         *      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 @@ static void second_overflow(void)
        break;
 
     case TIME_OOP:
+
        time_state = TIME_WAIT;
        break;
 
@@ -723,7 +762,7 @@ void do_timer(struct pt_regs * regs)
        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 @@ asmlinkage int sys_getegid(void)
        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;
 }
index d5f6d45a3f5d01e15a3354dd053c7960877485bd..e02e457d78bfc5be530697f4b2928c417caefbfb 100644 (file)
@@ -58,13 +58,24 @@ asmlinkage int sys_setpriority(int which, int who, int niceval)
 {
        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 @@ asmlinkage int sys_setpriority(int which, int who, int niceval)
        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 @@ asmlinkage int sys_getpriority(int which, int who)
                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 @@ asmlinkage int sys_setdomainname(char *name, int len)
        
        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)
index a8f81dad88eab85ecfa47dc2fce02b22d9088dc1..6ee85864a8bce5fa45b42c0b0b7f530d1e750d7b 100644 (file)
@@ -48,14 +48,21 @@ static inline void multi_bmap(struct inode * inode, unsigned long block, unsigne
        } 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);
 }
 
index 8fbd651b07d2d8cf4797c0743b3d3e2def8170d0..0dd87036271eef4f5ed0bf4457718f9218e82b42 100644 (file)
@@ -928,6 +928,114 @@ static int share_page(struct vm_area_struct * area, unsigned long address,
        return 0;
 }
 
+/*
+ * 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.
  */
index 394d044d7c415e85ef32621a9f812b582d404a6e..f605c9528662c4a48ae5d2b765165172867df02e 100644 (file)
@@ -250,9 +250,21 @@ o  Assorted driver/multicast fixes                 [IN]
 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   Tom May's insw_and_checksum()
 
 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   Zebedee
 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 @@ a maybe (so is finishing it ;))][Jim Freeman is working on Frame Relay].
 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]
@@ -390,6 +399,8 @@ their hands. [Provisionally taken]
 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
 kernel logger that is reliable. There is now a loadable module allowing
diff --git a/net/Config.in b/net/Config.in
new file mode 100644 (file)
index 0000000..c233ea2
--- /dev/null
@@ -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
index 75a57d5833a2a80d154c75ef530d97f4468904c3..4e92b434fa946ecd6154e849357c396ab579da22 100644 (file)
@@ -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 @@ static void aarp_send_query(struct aarp_entry *a)
         *      Send it.
         */     
         
-        
        dev_queue_xmit(skb, dev, SOPRI_NORMAL);
        
        /*
@@ -435,6 +434,7 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
        /*
         *      Non ELAP we cannot do.
         */
+
        if(dev->type!=ARPHRD_ETHER)
        {
                return -1;
@@ -466,6 +466,7 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
                /*
                 *      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 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
                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 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
                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 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
        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 @@ static void aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, int ha
                {
                        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 @@ static void aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, int ha
        }
 }
 
+/*
+ *     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 @@ static int aarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
                        /*
                         *      Fail the probe (in use)
                         */
+                        
                        ifa->status|=ATIF_PROBE_FAIL;
                        restore_flags(flags);
                        kfree_skb(skb, FREE_READ);
@@ -663,6 +696,7 @@ static int aarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
                        /*
                         *      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 @@ static int aarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
                        /*
                         *      aarp_my_address has found the address to use for us.
                         */
+                        
                        aarp_send_reply(dev,ma,&sa,ea->hw_src);
                        break;
        }
index a809f212e03601c5bdede46b8725285b3dad8b40..bcef24972f913e54f5ce865e8bdd122a8d3a9937 100644 (file)
@@ -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 @@ static atalk_socket *atalk_search_socket(struct sockaddr_at *to, struct atalk_if
 {
        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 @@ static atalk_socket *atalk_find_socket(struct sockaddr_at *sat)
 {
        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 @@ static void atalk_destroy_socket(atalk_socket *sk)
 }
 
 
-/* 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 @@ int atalk_get_info(char *buffer, char **start, off_t offset, int length, int dum
        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 @@ struct atalk_iface *atalk_find_dev(struct device *dev)
 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 @@ static int atrtr_create(struct rtentry *r, struct device *devhint)
                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 @@ static int atrtr_delete( struct at_addr *addr )
        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 @@ void atrtr_device_down(struct device *dev)
        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 @@ int atif_ioctl(int cmd, void *arg)
                        /*
                         *      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 @@ static int atalk_create(struct socket *sock, int protocol)
        sk->type=sock->type;
        sk->debug=0;
        
-       sk->at.src_net=0;
-       sk->at.src_node=0;
-       sk->at.src_port=0;
+       sk->protinfo.af_at.src_net=0;
+       sk->protinfo.af_at.src_node=0;
+       sk->protinfo.af_at.src_port=0;
        
-       sk->at.dest_net=0;
-       sk->at.dest_node=0;
-       sk->at.dest_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_release(struct socket *sock, struct socket *peer)
  
 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 @@ static int atalk_autobind(atalk_socket *sk)
        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 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
                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 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
                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 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
        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 @@ static int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
        {
                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 @@ int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
        /*
         *      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 @@ int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                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 @@ int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                        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 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
                        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 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
        {
                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 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
        *((__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 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
                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 @@ void atalk_proto_init(struct net_proto *pro)
                atalk_if_get_info
        });
 
-       printk("Appletalk BETA 0.13 for Linux NET3.031\n");
+       printk("Appletalk 0.14 for Linux NET3.032\n");
 }
 #endif
index 3b211b04c334805336d24f51321e6c9eaf88ff2f..8f793c7bd5fa87717092b9bb81f7336e9136dc8d 100644 (file)
 #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 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a
        /*
         *      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 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a
         *      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 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a
                        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 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a
                                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 @@ void ax25_proto_init(struct net_proto *pro)
                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 @@ void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
                *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 @@ int ax25_rebuild_header(unsigned char *bp, struct device *dev, unsigned long des
        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);
index 7c98519f886a2d757a9a5e02a7571c13398a601d..be1b33ae4053da4cd2ebd7f40914f198c2c17e1f 100644 (file)
@@ -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
index 4dc47269eab9121cfcd899c217caa23c191ff097..02723a9089fe4e45f9513fe045cd7f319e1cd1cf 100644 (file)
@@ -221,7 +221,7 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait)
                                /* 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);
                        }
index a8d71b1cce4672ae6e51010254765fb6a7759868..d9d38cde14cba1d543a760c50bb1e3f57bdbd5e6 100644 (file)
@@ -1370,4 +1370,3 @@ int net_dev_init(void)
        enable_bh(NET_BH);
        return 0;
 }
-
index 36ecad9cdfa11246ec547c90a1167d7a6f372f2c..dd7e721d429350be892c10cd26b606c978aa9f63 100644 (file)
@@ -194,4 +194,3 @@ void dev_mc_discard(struct device *dev)
        }
        dev->mc_count=0;
 }
-
diff --git a/net/core/firewall.c b/net/core/firewall.c
new file mode 100644 (file)
index 0000000..6bf6c84
--- /dev/null
@@ -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;
+}
index 54a9c3579c1ba60a213397575c49cafa8daa6386..135d3c903a471744e5e9eb87ce823aef8fb45473 100644 (file)
@@ -104,4 +104,3 @@ void memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
                iov++;
        }
 }
-
index 1b5addf6561924da01ec9f81fd3972a2f962c1eb..5164b3ea110a35d5d15bb304ce22ac98a0c4ce6c 100644 (file)
@@ -460,9 +460,9 @@ void kfree_skb(struct sk_buff *skb, int rw)
                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 @@ int skb_device_locked(struct sk_buff *skb)
 {
        return skb->lock? 1 : 0;
 }
-
index 05300833698a32180a32a506bf23bca7ad9e38f7..8d4617ed5be8a77f2a6a4ec8cb5fe9b4e3ee8d3a 100644 (file)
@@ -569,5 +569,3 @@ void release_sock(struct sock *sk)
        }
 #endif  
 }
-
-
diff --git a/net/ipv4/Config.in b/net/ipv4/Config.in
new file mode 100644 (file)
index 0000000..4938b60
--- /dev/null
@@ -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
index 20ceb9eecba54ac14da0e0bc88c008018230e50f..a86527cf698364b94d1c379eb12ed3716ace3fb4 100644 (file)
@@ -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 :=
index 638809a02dd36f909f377686bc19d8ad991ffe4f..0924a0a8fd9e5b083fee2af3fdee99aad9365598 100644 (file)
@@ -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 @@ unsigned short get_new_socknum(struct proto *prot, unsigned short base)
                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 @@ unsigned short get_new_socknum(struct proto *prot, unsigned short base)
 
 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 @@ void put_sock(unsigned short num, struct sock *sk)
        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 @@ void put_sock(unsigned short num, struct sock *sk)
                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 @@ void destroy_sock(struct sock *sk)
 
        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 @@ void destroy_sock(struct sock *sk)
                }
        }       
 
-       /* 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 @@ void destroy_sock(struct sock *sk)
                 * 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 @@ void destroy_sock(struct sock *sk)
        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 @@ static int inet_create(struct socket *sock, int protocol)
        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 @@ static int inet_dup(struct socket *newsock, struct socket *oldsock)
 }
 
 /*
- * 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 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
        }
        
        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 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
                        
                        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 @@ static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
        } 
        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 @@ static int inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
                             (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 @@ static int inet_sendto(struct socket *sock, const void *ubuf, int size, int nobl
                           (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 @@ struct sock *get_sock(struct proto *prot, unsigned short num,
                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 @@ struct sock *get_sock_raw(struct sock *sk,
                        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 @@ struct sock *get_sock_mcast(struct sock *sk,
                        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 @@ static struct proto_ops inet_proto_ops = {
        inet_setsockopt,
        inet_getsockopt,
        inet_fcntl,
+       inet_sendmsg,
+       inet_recvmsg
 };
 
 extern unsigned long seq_offset;
@@ -1554,7 +1625,7 @@ void inet_proto_init(struct net_proto *pro)
        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... 
index aa230b5db2adedb215caeabe4b9be00fe3e2a65a..5dee6338ae9f26c7cb7ee997f8b4c8b37df7c7ab 100644 (file)
@@ -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 @@ static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device
        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 @@ static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct de
         */
         
        {
-         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 @@ static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct de
        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 @@ static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct devi
        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 @@ void icmp_init(struct proto_ops *ops)
        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 --git a/net/ipv4/ip.c b/net/ipv4/ip.c
deleted file mode 100644 (file)
index 82fa168..0000000
+++ /dev/null
@@ -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 --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
new file mode 100644 (file)
index 0000000..b5544ce
--- /dev/null
@@ -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 --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
new file mode 100644 (file)
index 0000000..753f71f
--- /dev/null
@@ -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++;
+}
+
+
index 1c00aa80f4aaef4bad071f3957bba157a1fab5c4..2f9c485c6d81fd01295c3d4668a43abfadbb8f72 100644 (file)
@@ -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 @@ static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
   
 #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 @@ void ip_fw_init(void)
        });
 #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 --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
new file mode 100644 (file)
index 0000000..edd1bd2
--- /dev/null
@@ -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 --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
new file mode 100644 (file)
index 0000000..7103571
--- /dev/null
@@ -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 --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
new file mode 100644 (file)
index 0000000..3efd555
--- /dev/null
@@ -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 --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
new file mode 100644 (file)
index 0000000..6e16eab
--- /dev/null
@@ -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);
+}
index 3ddce285fc36f2524f628d7cb1e59a00c36567de..35ccad1bfed64df3ed4fb9944b3d5f392b8fd172 100644 (file)
@@ -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 @@ int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
        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);
index 8da50d5c5ed215cdcf6c918f7a50475b3e36bcc4..3005f7ca9d5fee7ef56b5adca32077ea7a0cad37 100644 (file)
@@ -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 @@ static unsigned long min(unsigned long a, unsigned long b)
 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 @@ int packet_rcv(struct sk_buff *skb, struct device *dev,  struct packet_type *pt)
        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 @@ int packet_rcv(struct sk_buff *skb, struct device *dev,  struct packet_type *pt)
         *      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 @@ int packet_rcv(struct sk_buff *skb, struct device *dev,  struct packet_type *pt)
  *     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 @@ static int packet_sendto(struct sock *sk, const unsigned char *from, int len,
         *      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 @@ static int packet_sendto(struct sock *sk, const unsigned char *from, int len,
        dev = dev_get(saddr->sa_data);
        if (dev == NULL) 
        {
-               return(-ENXIO);
+               return(-ENODEV);
        }
        
        /*
@@ -187,11 +165,12 @@ static int packet_sendto(struct sock *sk, const unsigned char *from, int len,
        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 @@ static int packet_sendto(struct sock *sk, const unsigned char *from, int len,
         
        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 @@ static int packet_sendto(struct sock *sk, const unsigned char *from, int len,
        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 @@ static int packet_init(struct sock *sk)
  *     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 @@ int packet_recvfrom(struct sock *sk, unsigned char *to, int len,
         
        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 @@ int packet_recvfrom(struct sock *sk, unsigned char *to, int len,
        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 @@ int packet_read(struct sock *sk, unsigned char *buff,
  
 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 @@ struct proto packet_prot =
        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",
index 7c04cdfdae51497fad296caad382bb4c71e1f069..dbe03db4e24aa9cd508aebc34fa793416f409efc 100644 (file)
@@ -266,13 +266,13 @@ static int raw_init(struct sock *sk)
  *     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 @@ int raw_recvfrom(struct sock *sk, unsigned char *to, int len,
 
        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_recvfrom(struct sock *sk, unsigned char *to, int len,
 }
 
 
-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 @@ struct proto raw_prot = {
        NULL,
        ip_setsockopt,
        ip_getsockopt,
+       NULL,
+       raw_recvmsg,
        128,
        0,
        "RAW",
index 843f99f92bbed6ebd0ad074393a27d304d3212d6..653cc3f2c6c11ac304c6e0cebcf3fdfde8165a95 100644 (file)
@@ -530,7 +530,7 @@ static __inline__ void tcp_set_state(struct sock *sk, int state)
    
 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 @@ static int tcp_select(struct sock *sk, int sel_type, select_table *wait)
                 * 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 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
                        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 @@ static void tcp_send_ack(u32 sequence, u32 ack,
         * 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 @@ static void tcp_send_ack(u32 sequence, u32 ack,
        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 @@ extern __inline int tcp_build_header(struct tcphdr *th, struct sock *sk, int pus
  *     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 @@ static int tcp_write(struct sock *sk, const unsigned char *from,
                                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 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.
+                */
 
-       /*
-        *      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;
-               }
+                       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 @@ static int tcp_write(struct sock *sk, const unsigned char *from,
        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 @@ static void tcp_read_wakeup(struct sock *sk)
         * 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 @@ static void tcp_read_wakeup(struct sock *sk)
        if (tmp < 0) 
        {
                buff->free = 1;
-               sk->prot->wfree(sk, buff);
+               sock_wfree(sk, buff);
                return;
        }
 
@@ -2085,7 +2120,7 @@ static void cleanup_rbuf(struct sock *sk)
        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 @@ static void cleanup_rbuf(struct sock *sk)
         */
 
        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 @@ static void cleanup_rbuf(struct sock *sk)
  *     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 @@ static int tcp_read_urg(struct sock * sk, int nonblock,
 
        if (sk->state == TCP_CLOSE || sk->done) 
        {
-               if (!sk->done) {
+               if (!sk->done) 
+               {
                        sk->done = 1;
                        return 0;
                }
@@ -2196,7 +2232,16 @@ static int tcp_read_urg(struct sock * sk, int nonblock,
                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 @@ static int tcp_read_urg(struct sock * sk, int nonblock,
  *     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 @@ static int tcp_read(struct sock *sk, unsigned char *to,
         */
         
        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 @@ static int tcp_read(struct sock *sk, unsigned char *to,
                 *      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 @@ static int tcp_read(struct sock *sk, unsigned char *to,
                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 @@ static int tcp_read(struct sock *sk, unsigned char *to,
        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 @@ static void tcp_send_fin(struct sock *sk)
                
        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 @@ static void tcp_send_fin(struct sock *sk)
                 */
                 
                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 @@ void tcp_shutdown(struct sock *sk, int how)
        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 @@ static void tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *t
         * 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 @@ static void tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *t
        if (tmp < 0) 
        {
                buff->free = 1;
-               prot->wfree(NULL, buff);
+               sock_wfree(NULL, buff);
                return;
        }
 
@@ -3068,7 +3120,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
        
        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 @@ extern __inline__ int tcp_data(struct sk_buff *skb, struct sock *sk,
         *      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 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
        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 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
                                        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 @@ static void tcp_write_wakeup(struct sock *sk)
                 *      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 @@ static void tcp_write_wakeup(struct sock *sk)
                                         sk->ip_tos,sk->ip_ttl);
                if (tmp < 0) 
                {
-                       sk->prot->wfree(sk, buff);
+                       sock_wfree(sk, buff);
                        return;
                }
                
@@ -5297,7 +5349,7 @@ static void tcp_write_wakeup(struct sock *sk)
        }
        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 @@ static void tcp_write_wakeup(struct sock *sk)
                                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 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *o
 
 
 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 @@ struct proto tcp_prot = {
        tcp_shutdown,
        tcp_setsockopt,
        tcp_getsockopt,
+       tcp_sendmsg,
+       tcp_recvmsg,
        128,
        0,
        "TCP",
index ef87a365febb8257df9c069cc35f5eb1d9e02c56..60f2ad130dbe2a81be1f744f00dcb5534d9acd7f 100644 (file)
@@ -425,7 +425,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
                        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 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
  *     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 @@ int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
         *      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 @@ int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
        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 @@ static int udp_deliver(struct sock *sk, struct udphdr *uh, struct sk_buff *skb,
 
 
 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 @@ struct proto udp_prot = {
        NULL,
        ip_setsockopt,
        ip_getsockopt,
+       NULL,
+       udp_recvmsg,
        128,
        0,
        "UDP",
index 2017598837f7e2a897f3a8027bf7b5f4a412ffab..3c1f9fdd3c7f0fcaf4ca4fbb1b37a3d461071c24 100644 (file)
@@ -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 
 #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_frame_name(unsigned short);
 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 @@ ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
        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 @@ ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
 
        /* 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 @@ ipxrtr_delete(long net)
        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 @@ ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, const void *ubuf
        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 @@ ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, const void *ubuf
        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 @@ ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, const void *ubuf
        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 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
        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 @@ static int ipx_sendto(struct socket *sock, const void *ubuf, int len, int nobloc
                        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 @@ static int ipx_sendto(struct socket *sock, const void *ubuf, int len, int nobloc
                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 @@ static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
        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 @@ static int ipx_write(struct socket *sock, const char *ubuf, int size, int nobloc
        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 @@ static struct proto_ops ipx_proto_ops = {
        ipx_setsockopt,
        ipx_getsockopt,
        ipx_fcntl,
+       ipx_sendmsg,
+       ipx_recvmsg
 };
 
 /* Called by ddi.c on kernel start up */
@@ -1990,7 +2073,7 @@ void ipx_proto_init(struct net_proto *pro)
                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
index ba55db7502d93db6f74a6bae00a79a06ac8fd4e2..ef155781c6c828c40e796a659305a3dfb9660b5d 100644 (file)
@@ -913,6 +913,7 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev)
        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 @@ void nr_proto_init(struct net_proto *pro)
 {
        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;
index b386479128f92dc1c486552ea6b921e87b2e6fbb..ff4bb73b44f8514144d981b27e047fc1ccd1c947 100644 (file)
@@ -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 @@ void nr_link_failed(ax25_address *callsign, struct device *dev)
  *     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 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
        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);
 
@@ -685,6 +695,11 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
        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;
 
index 626eef7d50209c8c1be49b3e7a0502d9728bfc75..db61c79406501933ed706f10bb3e12ee652dd0b1 100644 (file)
@@ -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 @@ void sock_init(void)
 {
        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. 
@@ -1310,6 +1311,14 @@ void sock_init(void)
        netlink_attach(NETLINK_ROUTE, netlink_donothing);
 #endif
 
+       /*
+        *      Attach the firewall module if configured
+        */
+        
+#ifdef CONFIG_FIREWALL  
+       fwchain_init();
+#endif
+
        /*
         *      Initialize the protocols module. 
         */
index d197ef35c1aee1de80df8c09613bb8cbe2e83352..ab67f0ca526f4519f47f186a8f776acb1f6da758 100644 (file)
@@ -99,11 +99,11 @@ function define_bool () {
 #
 # 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 @@ function bool () {
 #
 # 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 @@ function tristate () {
 #      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 @@ if [ "$1" != "" ] ; then
        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
index 5131cbb8810e9cab821f2c83a893cbe9949ede27..9f0173488cb4ca9ff1ef44f4c63c44e46f280d80 100644 (file)
@@ -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 read_config_file { w } {
 
 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_comment { file1 file2 text } {
 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} {
index 2981a999c10ddc5235584408f4e2d74f48b45fc5..84593a4002372bec78580dd1692418ab23e057f1 100644 (file)
@@ -65,7 +65,3 @@ if { [file exists include/linux/autoconf.h] == 1 } then {
                        .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
-       }
index 050ff057051700ab8593d0987f911acaabfb3838..21d011db7e451a25be174f53b65cf4a45f79fd45 100644 (file)
@@ -236,6 +236,7 @@ int fix_conditionals(struct kconfig * scfg)
   int depth = 0;
   int i;
   struct kconfig * cfg;
+  struct kconfig * cfg1;
   struct condition * conditions[25];
   struct condition * cnd;
   struct condition * cnd1;
@@ -302,6 +303,43 @@ int fix_conditionals(struct kconfig * scfg)
          break;
        }
     }
+  /*
+   * 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
@@ -328,6 +366,7 @@ int fix_conditionals(struct kconfig * scfg)
          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,
index 12b9dbceeb7895fede984e9a00d6d95054adb39f..e143bf967589b6fe3918dbad9c84043bcbe02572 100644 (file)
 #define FALSE (0)
 #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 @@ static start_proc(char * label, int menu_num, int flag)
   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);
 }
 
 /*
@@ -78,6 +90,14 @@ generate_if(struct kconfig * item,
       cond = cond->next;
     }
 
+  /*
+   * Now write this option.
+   */
+  if( (item->flags & GLOBAL_WRITTEN) == 0)
+    {
+       printf("\tglobal %s\n", item->optionname);
+    }
+
   /*
    * Now generate the body of the conditional.
    */
@@ -164,10 +184,12 @@ generate_if(struct kconfig * item,
       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 @@ generate_if(struct kconfig * item,
       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 @@ static end_proc(int menu_num, int first, int last)
    */
   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 @@ static end_proc(int menu_num, int first, int last)
   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 @@ dump_tk_script(struct kconfig *scfg)
   int menu_line = 0;
   int menu_maxlines = 0;
   struct kconfig * cfg;
+  char * menulabel;
 
   /*
    * Start by assigning menu numbers, and submenu numbers.
@@ -485,6 +528,13 @@ dump_tk_script(struct kconfig *scfg)
        };
     }
 
+  /*
+   * 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
@@ -509,6 +559,7 @@ dump_tk_script(struct kconfig *scfg)
            {
              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 @@ dump_tk_script(struct kconfig *scfg)
          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 @@ dump_tk_script(struct kconfig *scfg)
          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 @@ dump_tk_script(struct kconfig *scfg)
          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 @@ dump_tk_script(struct kconfig *scfg)
                 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,
index 59352f4c61b752f5c55574e9f7af73c977cae4b2..625724b8ef6b17fd2773e6afebdfdbc4fb3543d1 100644 (file)
@@ -26,6 +26,7 @@ struct kconfig * config = NULL;
 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 @@ int parse(char * pnt) {
     }
   else if (strncmp(pnt, "mainmenu_option", 15) == 0) 
     {
+      menus_seen++;
       tok = tok_menuoption;
       pnt += 15;
     }
@@ -441,6 +443,12 @@ main(int argc, char * argv[])
     }
 
 
+  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
index d1b758caaf2edf9fe187452754b9567aea65ecc3..45d3ebdd11fb2d2d41a88248d585db956a5988dd 100644 (file)
@@ -41,7 +41,9 @@ struct condition
   union var variable;
 };
 
-#define GLOBAL_WRITTEN 1
+#define GLOBAL_WRITTEN  1
+#define CFG_DUP        2
+
 struct kconfig
 {
   struct kconfig * next;