--- /dev/null
+$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;
+}
--- /dev/null
+ 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.
--- /dev/null
+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.
--- /dev/null
+ 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)
--- /dev/null
+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.
--- /dev/null
+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
--- /dev/null
+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)
--- /dev/null
+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;
+ }
+ }
+}
+/*==========================================================================*/
+
--- /dev/null
+ -- 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
--- /dev/null
+ 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.
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
VERSION = 1
PATCHLEVEL = 3
-SUBLEVEL = 35
+SUBLEVEL = 36
ARCH = i386
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
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 \
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
--- /dev/null
+#
+# 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
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
--- /dev/null
+#
+# 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
endif
endif
+ifdef CONFIG_CD_NO_IDESCSI
+SUB_DIRS += cdrom
+MOD_SUB_DIRS += cdrom
+endif
+
include $(TOPDIR)/Rules.make
--- /dev/null
+#
+# 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
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
+++ /dev/null
-$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;
-}
+++ /dev/null
- 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.
+++ /dev/null
-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.
+++ /dev/null
- 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)
- 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
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)
*** =================
"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.
================================================================================
+++ /dev/null
-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.
+++ /dev/null
-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
+++ /dev/null
-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)
+++ /dev/null
-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;
- }
- }
-}
-/*==========================================================================*/
-
+++ /dev/null
- -- 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
+++ /dev/null
- 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.
+++ /dev/null
-#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(¬Used)) 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(¬Used) < 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
+++ /dev/null
-#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 */
+++ /dev/null
-/*
- * 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, ¶ms[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
+++ /dev/null
-/* 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
* 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
int FLOPPY_IRQ=6;
int FLOPPY_DMA=2;
int ALLOWED_DRIVE_MASK = 0x33;
-int FDC1 = 0x3f0;
-int FDC2 = -1;
#endif
#define FLOPPY_IRQ 6
#define FLOPPY_DMA 2
-#define FDC1 0x3f0
-static int FDC2=-1;
#endif
#define MODULE_AWARE_DRIVER
#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)
{
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;
}
*/
#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()'
*/
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 */
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)){
{
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;
}
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;
}
}
#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();
}
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;
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;
DPRINT("floppy_stat reply overrun\n");
break;
}
- reply_buffer[i++] = inb_p(FD_DATA);
+ reply_buffer[i++] = fd_inb(FD_DATA);
}
}
FDCS->reset = 1;
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
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);
}
}
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);
sti();
floppy_enable_hlt();
- disable_dma(FLOPPY_DMA);
+ fd_disable_dma();
/* avoid dma going to a random drive after shutdown */
if(!initialising)
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;
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;
}
initialising=0;
if(have_no_fdc)
unregister_blkdev(MAJOR_NR,"fd");
+ else
+ virtual_dma_init();
return have_no_fdc;
}
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;
}
#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
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
+++ /dev/null
-#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
-
-
#include <asm/segment.h>
#define MAJOR_NR HD_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
static int revalidate_hddisk(kdev_t, int);
/*
- * 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
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)
static void init_dtc2278 (void)
{
+ unsigned long flags;
+
+ save_flags(flags);
cli();
#if SET_DTC2278_MODE4
/*
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:
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) {
* "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)
{
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;
* 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;
case -2: /* "serialize" */
do_serialize:
if (hw > 1) goto bad_hwif;
- single_threaded = 1;
+ serialized = 1;
goto done;
case -1: /* "noprobe" */
hwif->noprobe = 1;
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;
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;
}
void buggy_interface_fallback (int rc)
{
ide_pci_access_error (rc);
- single_threaded = 1;
+ serialized = 1;
disallow_unmask = 1;
printk("serialized, disabled unmasking\n");
}
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.. */
} else if (!(sreg & 1)) {
printk("not enabled\n");
} else {
-#endif /* 0 */
/*
* The first part is undocumented magic from the DOS driver.
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.
*/
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;
#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
* 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 *);
#include <asm/system.h>
#include <asm/io.h>
-#include "blk.h"
+#include <linux/blk.h>
/*
* The request-struct contains all necessary data
+++ /dev/null
-/*
- 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(¬Used) < 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
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/* $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);
-}
#include <asm/segment.h>
#define MAJOR_NR MEM_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
#define RAMDISK_MINOR 1
+++ /dev/null
-/*
- * 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:
- */
+++ /dev/null
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 2
-#include "sbpcd.c"
+++ /dev/null
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 3
-#include "sbpcd.c"
+++ /dev/null
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 4
-#include "sbpcd.c"
+++ /dev/null
-/* -- 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 );
-*/
-
-}
+++ /dev/null
-/*
- * 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, ¶ms[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 */
#include <asm/dma.h>
#define MAJOR_NR XT_DISK_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
XD_INFO xd_info[XD_MAXDRIVES];
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+#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(¬Used)) 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(¬Used) < 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
--- /dev/null
+/*
+ * 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, ¶ms[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
--- /dev/null
+/* 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
--- /dev/null
+#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
+
+
--- /dev/null
+/*
+ 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(¬Used) < 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 2
+#include "sbpcd.c"
--- /dev/null
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 3
+#include "sbpcd.c"
--- /dev/null
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 4
+#include "sbpcd.c"
--- /dev/null
+/* -- 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 );
+*/
+
+}
--- /dev/null
+/*
+ * 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, ¶ms[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 */
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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
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 ();
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);
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;
* 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.
#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>
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>
};
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"),
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")
#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"
--- /dev/null
+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
#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"
#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"
#if defined(__KERNEL__)
-#include "../block/blk.h"
+#include <linux/blk.h>
#include "scsi.h"
#include <asm/io.h>
#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 <asm/system.h>
#include <asm/io.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
#include "scsi.h"
#include "hosts.h"
#include "sd.h"
*
* -- 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
#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"
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
/*
* 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
*
* 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
* 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
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.
* 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).
#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
#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
#define SEQADDR1(x) ((x) + 0xC63ul)
#define ACCUM(x) ((x) + 0xC64ul) /* accumulator */
+#define SINDEX(x) ((x) + 0xC65ul)
/*
* Board Control (p. 3-43)
#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.
#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
/*
* 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
#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 */
#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
* 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)
/*
* 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;
/*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
#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;
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 {
*/
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;
(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)
(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
/*
}
}
+#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
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
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
/*+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*************************************************************************
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;
}
}
- 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))
{
}
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:
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",
}
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;
*/
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
/*
* 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)
{
/*
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;
}
}
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;
}
* 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
{
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"
:"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
* 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;
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
{
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;
}
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
{
*/
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
{
}
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)));
{
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");
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
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);
{
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;
}
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])
{
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)));
}
}
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)
{
*/
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);
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;
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));
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);
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);
}
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;
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;
* 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? */
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? */
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;
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);
}
/*
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",
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;
*/
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)
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;
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));
}
*/
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
{
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
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
*/
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));
#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
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?
/*
* 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));
*/
aic7xxx_spurious_count = 0;
- index += 1;
+ index++;
}
}
}
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
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
{
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
* 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
{
#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));
+ }
}
}
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;
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;
scb->cmd = cmd;
aic7xxx_position(cmd) = scb->position;
+#if 0
+ debug_scb(scb);
+#endif;
/*
* Construct the SCB beforehand, so the sequencer is
* 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
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*************************************************************************
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*************************************************************************
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*************************************************************************
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;
* 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
NULL, \
NULL, \
NULL, \
- NULL, \
+ aic7xxx_proc_info, \
NULL, \
aic7xxx_detect, \
NULL, \
extern const char *aic7xxx_info(struct Scsi_Host *);
+extern int aic7xxx_proc_info(char *, char **, off_t, int, int, int);
+
#endif /* _aic7xxx_h */
# 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
SELDI = 0x20
CLRSINT1 = 0x0c
SSTAT1 = 0x0c
+PHASEMIS = 0x10
SIMODE1 = 0x11
SCSIBUSL = 0x12
SHADDR = 0x14
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
# (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
# 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.
# 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.
#
# 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
FLAGS = 0x53 # Device configuration flags
TWIN_BUS = 0x01
WIDE_BUS = 0x02
+DPHASE = 0x04
MAX_OFFSET = 0x08
ACTIVE_MSG = 0x20
IDENTIFY_SEEN = 0x40
# 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.
# 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
# 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
# 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
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:
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
# 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
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
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
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
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.
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
# 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
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),
# 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
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
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
# 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
# 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
dma:
mov DFCNTRL,SINDEX
dma1:
-dma2:
test SSTAT0,0x1 jnz dma3 # DMADONE
test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun
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:
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.
#
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
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
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.
# 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
* 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>
--- /dev/null
+/*+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:
+ */
#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"
#endif
#include <linux/config.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
#include <linux/kernel.h>
#include "scsi.h"
#include "hosts.h"
#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"
#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"
#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>
#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"
#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"
#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>
#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"
#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"
#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>
#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"
#include <linux/module.h>
#endif
-#include "../block/blk.h"
+#include <linux/blk.h>
#include "scsi.h"
#include "hosts.h"
#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"
#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"
#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"
#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"
#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"
#include <asm/segment.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
#include "scsi.h"
#include "scsi_ioctl.h"
#include "hosts.h"
#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"
#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"
#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"
#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"
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
#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"
SCpnt->request_bufflen);
if (driver_byte(result) & DRIVER_SENSE)
print_sense("st", SCpnt);
+ else
+ printk("\n");
}
#endif
scode = sense[2] & 0x0f;
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 &&
}
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)
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));
}
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);
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,
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 */
#endif
STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */
(STp->buffer)->last_result_fatal = 0; /* Prevent error propagation */
+ STp->drv_write_prot = 0;
}
else {
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 */
(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
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,
struct wait_queue * waiting;
Scsi_Device* device;
unsigned char dirty;
- unsigned char write_pending;
unsigned char rw;
unsigned char ready;
unsigned char eof;
unsigned char density;
unsigned char door_locked;
ST_buffer * buffer;
+ struct semaphore sem;
int block_size;
int min_block;
int max_block;
struct mtget * mt_status;
Scsi_Cmnd SCpnt;
#if DEBUG
+ unsigned char write_pending;
int nbr_finished;
int nbr_waits;
#endif
#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"
#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"
#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"
#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"
--- /dev/null
+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
-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).
--- /dev/null
+#
+# 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 ;;
+
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
#
$(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)
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)
# @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
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
+
+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...
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.
* 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).
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
(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.
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;
}
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)
{
}
}
-#endif
int
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,
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);
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;
}
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;
}
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;
-#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
{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},
"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",
"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);
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[])
{
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
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);
+ }
}
}
}
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
{
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");
}
}
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))
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))
{
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"
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);
}
*/
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,
snd_installed_cards[i].config.dma2);
else
printk ("\n");
-#endif
}
else
snd_installed_cards[i].enabled = 0; /*
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},
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
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 */
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 */
{
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;
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);
gus_select_voice (voice);
- current = gus_read16 (0x09) >> 4;
+ curr = gus_read16 (0x09) >> 4;
target = voices[voice].initial_volume;
if (ramp_time == INSTANT_RAMP)
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);
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
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);
init_envelope (voice);
}
else
- compute_and_set_volume (voice, volume, 0);
+ {
+ compute_and_set_volume (voice, volume, 0);
+ }
save_flags (flags);
cli ();
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);
* 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;
* 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 ();
{
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)
{
}
- {
- 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 ();
}
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++)
{
}
}
- printk ("Maui: Transmit timeout\n");
+ printk ("Maui: Write timeout\n");
return 0;
}
* 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)
{
{
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;
* 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)
{
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)
{
}
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)
{
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++;
}
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++;
}
}
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;
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;
{
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;
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 =
{
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));
}
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,
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 */
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;
}
if (curr_ticks >= next_event_time)
{
next_event_time = 0xffffffff;
- sequencer_timer ();
+ sequencer_timer (0);
}
}
{
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)
{
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;
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 */
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)
{
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;
}
#define ALLOW_SELECT
#define ALLOW_BUFFER_MAPPING
+#undef NO_INLINE_ASM
+#undef SHORT_BANNERS
#ifdef MODULE
#include <linux/config.h>
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;
}
return -EIO;
}
- memcpy_fromfs ((mbox[dev]), &((buf)[0]), (4));
+ memcpy_fromfs (mbox[dev], &((buf)[0]), 4);
if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
{
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))
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;
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;
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 ();
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;
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 ();
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 ();
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 ();
restore_flags (flags);
- memcpy_tofs (&(((char *) arg)[0]), (&buf), (sizeof (buf)));
+ memcpy_tofs ((&((char *) arg)[0]), &buf, sizeof (buf));
return 0;
}
break;
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;
irq_mode = IMODE_NONE;
dsp_busy = 1;
+ trigger_bits = irq_mode;
return 0;
}
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)
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;
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)
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;
while (c >= 4)
{
- memcpy_fromfs ((event), &((buf)[p]), (4));
+ memcpy_fromfs (event, &((buf)[p]), 4);
ev_code = event[0];
if (ev_code == SEQ_FULLSIZE)
return count - c;
}
- memcpy_fromfs ((&event[4]), &((buf)[p + 4]), (4));
+ memcpy_fromfs (&event[4], &((buf)[p + 4]), 4);
}
else
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)
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 ();
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;
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)
return err;
}
- memcpy_tofs (&(((char *) arg)[0]), ((char *) inf), (sizeof (*inf)));
+ memcpy_tofs ((&((char *) arg)[0]), (char *) inf, sizeof (*inf));
kfree (inf);
return 0;
}
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)
return err;
}
- memcpy_tofs (&(((char *) arg)[0]), ((char *) inf), (sizeof (*inf)));
+ memcpy_tofs ((&((char *) arg)[0]), (char *) inf, sizeof (*inf));
kfree (inf);
return 0;
}
void
-sequencer_timer (void)
+sequencer_timer (unsigned long dummy)
{
seq_startplay ();
}
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;
}
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);
#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
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;
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 ();
if (curr_ticks >= next_event_time)
{
next_event_time = 0xffffffff;
- sequencer_timer ();
+ sequencer_timer (0);
}
}
#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)
{
#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 */
return err;
}
+ chrdev_registered = 1;
soundcard_init ();
if (sound_num_blocks >= 1024)
{
int i;
- unregister_chrdev (SOUND_MAJOR, "sound");
+ if (chrdev_registered)
+ unregister_chrdev (SOUND_MAJOR, "sound");
sound_stop_timer ();
sound_unload_drivers ();
sound_free_dma (i);
}
-#ifndef EXCLUDE_PNP
- sound_pnp_disconnect ();
-#endif
-
}
}
#endif
}
#ifndef EXCLUDE_SEQUENCER
+
+
+static struct timer_list seq_timer =
+{NULL, NULL, 0, 0, sequencer_timer};
+
void
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
void
sound_stop_timer (void)
{
- timer_table[SOUND_TIMER].expires = 0;
- timer_active &= ~(1 << SOUND_TIMER);
+ del_timer (&seq_timer);;
}
#ifndef EXCLUDE_AUDIO
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;
if (curr_ticks >= next_event_time)
{
next_event_time = 0xffffffff;
- sequencer_timer ();
+ sequencer_timer (0);
}
}
}
{-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))
{
}
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)
{
--- /dev/null
+#
+# 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
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;
}
-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)
}
}
+/*
+ * Release a buffer head
+ */
void brelse(struct buffer_head * buf)
{
if (!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.
#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
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;
*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++;
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;
}
*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++;
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;
update the cache.
*/
file_cluster = 0;
- if ((current = MSDOS_I(inode)->i_start) != 0) {
- cache_lookup(inode,INT_MAX,&last,¤t);
+ 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;
}
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,¤t,&nr,NULL,
+ if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
NULL)) < 0) {
if (!locked) unlock_creation();
return error;
#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
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;
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);
}
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;
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;
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;
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)
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;
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,
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;
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",
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,
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/uio.h>
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)
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);
}
--- /dev/null
+/*
+ * 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 */
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)
--- /dev/null
+/*
+ * 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 */
#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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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 */
* For definitions of the flags field, see tty.h
*/
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
struct cyclades_port {
int magic;
int type;
--- /dev/null
+#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
#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);
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);
*/
/*
- * ==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
*/
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);
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);
* 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)
#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.
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
};
*/
#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, \
* 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;
/* 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);
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 *,
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 */
* 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,
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
return 0;
}
-/* declarations from timer.c */
+/*
+ * Declarations from timer.c
+ */
+
extern struct sock *timer_base;
extern void delete_timer (struct sock *);
extern void net_timer (unsigned long);
-/* Enable debug/info messages */
+/*
+ * Enable debug/info messages
+ */
#define NETDEBUG(x) x
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
setup();
+#ifdef CONFIG_SMP
+ /*
+ * With the devices probed and setup we can
+ * now enter SMP mode.
+ */
+
+ smp_begin();
+#endif
+
#ifdef CONFIG_UMSDOS_FS
{
/*
#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>
/* 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),
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),
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.
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 */
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;
}
* Allocate process to CPU
*/
- next->processor = smp_processor_id();
+ next->processor = this_cpu;
+ next->last_processor = this_cpu;
#endif
if (current != next) {
break;
case TIME_OOP:
+
time_state = TIME_WAIT;
break;
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++;
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;
}
{
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))
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;
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;
}
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)
} 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);
}
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.
*/
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 -------------
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() ??
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
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]
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
--- /dev/null
+#
+# 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
* 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.
* Send it.
*/
-
dev_queue_xmit(skb, dev, SOPRI_NORMAL);
/*
/*
* Non ELAP we cannot do.
*/
+
if(dev->type!=ARPHRD_ETHER)
{
return -1;
/*
* 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)
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)
{
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;
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;
{
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;
}
}
+/*
+ * 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;
/*
* Fail the probe (in use)
*/
+
ifa->status|=ATIF_PROBE_FAIL;
restore_flags(flags);
kfree_skb(skb, FREE_READ);
/*
* 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)
/*
* aarp_my_address has found the address to use for us.
*/
+
aarp_send_reply(dev,ma,&sa,ea->hw_src);
break;
}
* 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
#include <linux/atalk.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
+#include <linux/firewall.h>
#ifdef CONFIG_ATALK
{
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 );
}
{
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 );
}
}
-/* 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;
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);
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;
}
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)
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;
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));
}
/*
* 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)
{
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;
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;
}
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;
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)
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;
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);
{
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));
/*
* 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))
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 */
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;
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 */
{
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;
*((__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);
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
atalk_if_get_info
});
- printk("Appletalk BETA 0.13 for Linux NET3.031\n");
+ printk("Appletalk 0.14 for Linux NET3.032\n");
}
#endif
#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>
/*
* 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;
* 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
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;
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 {
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);
*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 */
}
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);
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
/* 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);
}
enable_bh(NET_BH);
return 0;
}
-
--- /dev/null
+/*
+ * 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;
+}
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
{
return skb->lock? 1 : 0;
}
-
--- /dev/null
+#
+# 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
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 :=
* 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.
* 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
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;
void put_sock(unsigned short num, struct sock *sk)
{
- struct sock *sk1;
- struct sock *sk2;
+ struct sock **skp, *tmp;
int mask;
unsigned long flags;
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();
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();
}
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);
}
}
- /* 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; )
{
* 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);
}
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;
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;
}
/*
- * 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)
}
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. */
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)
{
}
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);
(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)
(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)
{
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++;
}
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);
}
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);
}
inet_setsockopt,
inet_getsockopt,
inet_fcntl,
+ inet_sendmsg,
+ inet_recvmsg
};
extern unsigned long seq_offset;
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...
* of thing. (OK)
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/kernel.h>
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);
}
*/
{
- 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 *)×[0], icmph+1, 4); /* Incoming stamp */
icmp_param.data_ptr=×
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);
}
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);
}
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 */
+++ /dev/null
-/*
- * 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
-}
-
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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++;
+}
+
+
#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>
#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
});
#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,
--- /dev/null
+/*
+ * 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);
+}
+
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+}
+
--- /dev/null
+/*
+ * 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);
+}
#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.
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);
* 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
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
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);
* 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.
*/
* 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.
* 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
dev = dev_get(saddr->sa_data);
if (dev == NULL)
{
- return(-ENXIO);
+ return(-ENODEV);
}
/*
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)
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 */
/*
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.
* 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);
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;
/*
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
struct proto packet_prot =
{
- sock_wmalloc,
- sock_rmalloc,
- sock_wfree,
- sock_rfree,
- sock_rspace,
- sock_wspace,
packet_close,
packet_read,
packet_write,
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",
* 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;
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. */
}
-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,
NULL,
ip_setsockopt,
ip_getsockopt,
+ NULL,
+ raw_recvmsg,
128,
0,
"RAW",
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);
* 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;
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;
* 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)
{
/*
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));
* 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);
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;
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);
}
* 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. */
if (tmp < 0)
{
buff->free = 1;
- sk->prot->wfree(sk, buff);
+ sock_wfree(sk, buff);
return;
}
save_flags(flags);
cli();
- left = sk->prot->rspace(sk);
+ left = sock_rspace(sk);
/*
* We have to loop through all the buffer headers,
*/
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
* 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
if (sk->state == TCP_CLOSE || sk->done)
{
- if (!sk->done) {
+ if (!sk->done)
+ {
sk->done = 1;
return 0;
}
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;
}
* 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;
*/
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
* 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
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;
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
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)
*/
buff->free = 1;
- prot->wfree(sk,buff);
+ sock_wfree(sk,buff);
sk->write_seq++;
t=del_timer(&sk->timer);
if(t)
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.
*/
* 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;
if (tmp < 0)
{
buff->free = 1;
- prot->wfree(NULL, buff);
+ sock_wfree(NULL, buff);
return;
}
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;
* 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)
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);
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);
}
* 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);
sk->ip_tos,sk->ip_ttl);
if (tmp < 0)
{
- sk->prot->wfree(sk, buff);
+ sock_wfree(sk, buff);
return;
}
}
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;
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;
}
struct proto tcp_prot = {
- sock_wmalloc,
- sock_rmalloc,
- sock_wfree,
- sock_rfree,
- sock_rspace,
- sock_wspace,
tcp_close,
tcp_read,
tcp_write,
tcp_shutdown,
tcp_setsockopt,
tcp_getsockopt,
+ tcp_sendmsg,
+ tcp_recvmsg,
128,
0,
"TCP",
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)
* 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
* 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. */
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
*/
struct proto udp_prot = {
- sock_wmalloc,
- sock_rmalloc,
- sock_wfree,
- sock_rfree,
- sock_rspace,
- sock_wspace,
udp_close,
udp_read,
udp_write,
NULL,
ip_setsockopt,
ip_getsockopt,
+ NULL,
+ udp_recvmsg,
128,
0,
"UDP",
* 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;
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),
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))
/* 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);
}
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;
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;
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;
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);
}
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;
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;
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;
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)
{
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)
ipx_setsockopt,
ipx_getsockopt,
ipx_fcntl,
+ ipx_sendmsg,
+ ipx_recvmsg
};
/* Called by ddi.c on kernel start up */
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
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 */
{
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;
* 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
#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;
* 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;
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);
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;
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
+#include <linux/firewall.h>
#include <net/netlink.h>
{
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.
netlink_attach(NETLINK_ROUTE, netlink_donothing);
#endif
+ /*
+ * Attach the firewall module if configured
+ */
+
+#ifdef CONFIG_FIREWALL
+ fwchain_init();
+#endif
+
/*
* Initialize the protocols module.
*/
#
# 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"
;;
#
# 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"
;;
# 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"
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
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.
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}
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} {
.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
- }
int depth = 0;
int i;
struct kconfig * cfg;
+ struct kconfig * cfg1;
struct condition * conditions[25];
struct condition * cnd;
struct condition * cnd1;
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
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,
#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.
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);
}
/*
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.
*/
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;
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:
*/
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");
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");
/*
int menu_line = 0;
int menu_maxlines = 0;
struct kconfig * cfg;
+ char * menulabel;
/*
* Start by assigning menu numbers, and submenu numbers.
};
}
+ /*
+ * 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
{
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;
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",
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",
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",
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,
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.
*/
}
else if (strncmp(pnt, "mainmenu_option", 15) == 0)
{
+ menus_seen++;
tok = tok_menuoption;
pnt += 15;
}
}
+ 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
union var variable;
};
-#define GLOBAL_WRITTEN 1
+#define GLOBAL_WRITTEN 1
+#define CFG_DUP 2
+
struct kconfig
{
struct kconfig * next;