diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd --- v1.3.35/linux/Documentation/cdrom/aztcd Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/aztcd Sun Oct 15 20:02:23 1995 @@ -0,0 +1,793 @@ +$Id: README.aztcd,v 1.80 1995/10/11 19:37:49 root Exp root $ + Readme-File README.aztcd + for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 + CD-ROM Driver + Version 1.7 and newer + (for other drives see 6.-8.) + +NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE + A PROPRIETARY INTERFACE (implemented on a sound card or on a + ISA-AT-bus card). + IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE, + such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE + WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE + USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER ! + THE STANDARD-KERNEL 1.2.x NOW ALSO SUPPORTS IDE-CDROM-DRIVES, SEE THE + HARDDISK (!) SECTION OF make config, WHEN COMPILING A NEW KERNEL!!! +---------------------------------------------------------------------------- + +Contents of this file: + 1. NOTE + 2. INSTALLATION + 3. CONFIGURING YOUR KERNEL + 4. RECOMPILING YOUR KERNEL + 4.1 AZTCD AS A RUN-TIME LOADABLE MODULE + 4.2 CDROM CONNECTED TO A SOUNDCARD + 5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS + 5.1 MULTISESSION SUPPORT + 5.2 STATUS RECOGNITION + 5.3 DOSEMU's CDROM SUPPORT + 6. BUG REPORTS + 7. OTHER DRIVES + 8. IF YOU DON'T SUCCEED ... DEBUGGING + 9. TECHNICAL HISTORY OF THE DRIVER + 10. ACKNOWLEDGMENTS + 11. PROGRAMMING ADD ONS: CDPLAY.C + APPENDIX: Source code of cdplay.c +---------------------------------------------------------------------------- + +1. NOTE +This software has been successfully in alpha and beta test for quite a long +time with AZTECH CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110 +and has proven to be stable with kernel versions 1.0.9 to 1.2.0. But with +any software there still may be bugs in it. So if you encounter problems, +you are invited to help us improve this software. Please send me a detailed +bug report (see chapter BUG REPORTS). You are also invited in helping us to +increase the number of drives, which are supported. + +Please read the README-files carefully and always keep a backup copy of your +old kernel, in order to reboot if something goes wrong! + + +2. INSTALLATION +If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz +(xx=version number) and not included in the standard Linux kernel, read the +file AZTECH.CDROM.README included in that package to install it. The +standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'. +The driver consists of a header file 'aztcd.h', which normally should reside +in /usr/include/linux and the source code 'aztcd.c', which normally resides in +/usr/src/linux/drivers/block. It uses /dev/aztcd0, which must be a valid block +device with major number 29 and reside in directory /dev. To mount a CD-ROM, +your kernel needs to have the ISO9660-filesystem support included. + + +3. CONFIGURING YOUR KERNEL +If your kernel is already configured for using the AZTECH driver you will +see the following message while Linux boots: + Aztech CD-ROM Init: DriverVersion= BaseAddress= + Aztech CD-ROM Init: FirmwareVersion=>> + Aztech CD-ROM Init: 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=, 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=,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= BaseAddress= + Aztech CD-ROM Init: FirmwareVersion= + Aztech CD-ROM Init: 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= +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=,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 +#include +#include +#include +#include +#include +#include +#include + +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_trk0last) 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_tracklast) entry.cdte_track=last; + entry.cdte_format=CDROM_MSF; + if (ioctl(handle,cmd,&entry)) + { printf("Drive error or invalid track no.\n"); + } + else + { printf("Mode %d Track, starts at %d:%d:%d\n", \ + entry.cdte_adr,entry.cdte_addr.msf.minute, \ + entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame); + } + } + break; + case 'a': cmd=CDROMPLAYMSF; + printf("Address (min:sec:frame) "); + scanf("%d:%d:%d",&arg1,&arg2,&arg3); + msf.cdmsf_min0 =arg1; + msf.cdmsf_sec0 =arg2; + msf.cdmsf_frame0=arg3; + if (msf.cdmsf_sec0 > 59) msf.cdmsf_sec0 =59; + if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74; + msf.cdmsf_min1=60; + msf.cdmsf_sec1=00; + msf.cdmsf_frame1=00; + if (ioctl(handle,cmd,&msf)) + { printf("Drive error or invalid address\n"); + } + break; +#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/ + case 'd': cmd=CDROMREADCOOKED; + printf("Address (min:sec:frame) "); + scanf("%d:%d:%d",&arg1,&arg2,&arg3); + azt.msf.cdmsf_min0 =arg1; + azt.msf.cdmsf_sec0 =arg2; + azt.msf.cdmsf_frame0=arg3; + if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59; + if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74; + if (ioctl(handle,cmd,&azt.msf)) + { printf("Drive error, invalid address or unsupported command\n"); + } + k=0; + getchar(); + for (i=0;i<128;i++) + { printf("%4d:",i*16); + for (j=0;j<16;j++) + { printf("%2x ",azt.buf[i*16+j]); + } + for (j=0;j<16;j++) + { if (isalnum(azt.buf[i*16+j])) + printf("%c",azt.buf[i*16+j]); + else + printf("."); + } + printf("\n"); + k++; + if (k>=20) + { printf("press ENTER to continue\n"); + getchar(); + k=0; + } + } + break; + case 'w': cmd=CDROMREADRAW; + printf("Address (min:sec:frame) "); + scanf("%d:%d:%d",&arg1,&arg2,&arg3); + azt.msf.cdmsf_min0 =arg1; + azt.msf.cdmsf_sec0 =arg2; + azt.msf.cdmsf_frame0=arg3; + if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59; + if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74; + if (ioctl(handle,cmd,&azt)) + { printf("Drive error, invalid address or unsupported command\n"); + } + k=0; + for (i=0;i<147;i++) + { printf("%4d:",i*16); + for (j=0;j<16;j++) + { printf("%2x ",azt.buf[i*16+j]); + } + for (j=0;j<16;j++) + { if (isalnum(azt.buf[i*16+j])) + printf("%c",azt.buf[i*16+j]); + else + printf("."); + } + printf("\n"); + k++; + if (k>=20) + { getchar(); + k=0; + } + } + break; +#endif + case 'v': cmd=CDROMVOLCTRL; + printf("--Channel 0 Left (0-255): "); + scanf("%d",&arg1); + printf("--Channel 1 Right (0-255): "); + scanf("%d",&arg2); + volctrl.channel0=arg1; + volctrl.channel1=arg2; + volctrl.channel2=0; + volctrl.channel3=0; + if (ioctl(handle,cmd,&volctrl)) + { printf("Drive error or unsupported command\n"); + } + break; + case 'q': if (close(handle)) printf("Drive Error: CLOSE\n"); + exit(0); + case 'h': help(); + break; + default: printf("unknown command\n"); + break; + } + } + } + return 0; +} diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/cdu31a linux/Documentation/cdrom/cdu31a --- v1.3.35/linux/Documentation/cdrom/cdu31a Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/cdu31a Sat Jul 1 19:05:58 1995 @@ -0,0 +1,160 @@ + Tips for using cdu31a.c, the driver for Sony CDU-31A and CDU-33A CDROM + drives. + + Corey Minyard (minyard@wf-rch.cirr.com) + + Colossians 3:17 + + The Sony interface device driver handles Sony interface CDROM + drives and provides a complete block-level interface as well as an + ioctl() interface compatible with the Sun (as specified in + include/linux/cdrom.h). With this interface, CDROMs can be + accessed and standard audio CDs can be played back normally. + + WARNING - All autoprobes have been removed from the driver. + You MUST configure the CDU31A via a LILO config + at boot time or in lilo.conf. I have the + following in my lilo.conf: + + append="cdu31a=0x1f88,0,PAS" + + The first number is the I/O base address of the + card. The second is the interrupt (0 means none). + The third should be "PAS" if on a Pro-Audio + spectrum, or nothing if on something else. + + This interface is (unfortunately) a polled interface. This is + because most Sony interfaces are set up with DMA and interrupts + disables. Some (like mine) do not even have the capability to + handle interrupts or DMA. For this reason you will see a lot of + the following: + + retry_count = jiffies+ SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (! ' +The driver should report his results now. + +That's it! Mount a disk, i.e. 'mount -rt iso9660 /dev/gscd0 /cdrom' + +Feel free to report errors and suggestions to the following address. +Be sure, I'm very happy to receive your comments! + + Oliver Raupach Hannover, Juni 1995 +(raupach@nwfs1.rz.fh-hannover.de) diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/mcd linux/Documentation/cdrom/mcd --- v1.3.35/linux/Documentation/cdrom/mcd Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/mcd Tue Aug 29 07:01:32 1995 @@ -0,0 +1,4 @@ +This driver does not support XA or MultiSession CDs (PhotoCDs). Use the +experimental driver mcdx.c for that. + +You can use mcd for one interface, and mcdx for another. diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/mcdx linux/Documentation/cdrom/mcdx --- v1.3.35/linux/Documentation/cdrom/mcdx Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/mcdx Tue Aug 29 07:01:32 1995 @@ -0,0 +1,44 @@ +This is a first attempt to create an `improved' driver for the Mitsumi drives. +It is able to "live together" with mcd.c, if you have at least two Mitsumi +drives: each driver can use his own drive. + +To allow this "coexistence" as long as mcdx.c is not a superset of mcd.c, +this driver has to use its own device files. We use MAJOR 20 for it. So, +you have to do + + # mknod /dev/mcdx0 b 20 0 + # mknod /dev/mcdx1 b 20 1 + +and so on, one entry for each drive to support, once. + +If you are using the driver as a module, you can specify your ports and IRQs +like + + # insmod mcdx.o mcdx=0x300,11,0x304,5 + +and so on ("address,IRQ" pairs). +This will override the configuration in mcdx.h. + +This driver: + + o handles XA (and hopefully) multi session CDs as well as + ordinary CDs; + o supports up to 5 drives (of course, you'll need free + IRQs, i/o ports and slots); + o uses much less kernel memory than the standard mcd driver + (no extra driver internal buffers!). + o plays audio (like the `old' driver, I hope) + +This version doesn't support yet: + + o shared IRQs (but it seems to be possible - I've successfully + connected two drives to the same irq. So it's `only' a + problem of the driver.) + +This driver never will: + + o Read digital audio (i.e. copy directly), due to missing + hardware features. + + +heiko@lotte.sax.de diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/optcd linux/Documentation/cdrom/optcd --- v1.3.35/linux/Documentation/cdrom/optcd Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/optcd Mon Aug 28 09:58:20 1995 @@ -0,0 +1,46 @@ +This is the README file for the Optics Storage 8000 AT CDROM device driver. + +The driver contains code to enable an ISP16 interface if it finds one. It +didn't originally (although this README erroneously said so), because I think +this kind of code should go into its own module. But having to use a hack all +the time in order to use a part of the standard kernel started to annoy me, so +I copied the ISP16 code by Eric van der Maarel (maarel@marin.nl) from Vadim +Model's Sanyo sjcd driver. I'll remove it again from this driver when we have +some common way to talk to ISP16 interfaces. + +My original configuration code for the ISP-16 card can get found at + dutette.et.tudelft.nl:/pub/linux/ +and at Eberhard's mirror + ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/ + +Much more elaborate information can be found at ftp:rbrf.msk.su, +where Vadim Model (vadim@rbrf.msk.su) has made available an ISP16 +device driver. +Vadim's directory is + rbrf.msk.su:/linux/mediamagic/ +and Eberhard is holding a mirror at + ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/ + + +Before you can use the driver, you have to create the device file once: + # mknod /dev/optcd0 b 17 0 + +To specify the base address if the driver is "compiled-in" to your kernel, +you can use the kernel command line item (LILO option) + optcd=0x340 +with the right address. + +If you have compiled optcd as a module, you can load it with + # insmod /usr/src/linux/modules/optcd.o +or + # insmod /usr/src/linux/modules/optcd.o optcd=0x340 +with the matching address value of your interface card. + +I have tried the module with several 1.2.x kernel versions, and it seems to +work, as far as I tested. It also seems to work for several 1.3.x versions. +If you use it, I'd appreciate success/failure reports. If you find a bug, +try recompiling the driver with some strategically chosen #undef DEBUG_...'s +changed into #defines (you'll find them in .../include/linux/optcd.h) and +include the messages generated in your bug report. Good luck. + +Leo Spiekman (spiekman@dutette.et.tudelft.nl) diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/sbpcd linux/Documentation/cdrom/sbpcd --- v1.3.35/linux/Documentation/cdrom/sbpcd Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/sbpcd Thu Sep 14 09:46:22 1995 @@ -0,0 +1,1049 @@ +This README belongs to release 3.9 or newer of the SoundBlaster Pro +(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC) +CD-ROM driver for Linux. + +Sbpcd really, really is NOT for ANY IDE/ATAPI drive! +Not even if you have an "original" SoundBlaster card with an IDE interface! +So, you better have a look into README.ide if your port address is 0x1F0, +0x170, 0x1E8, 0x168 or similar. +I get tons of mails from IDE/ATAPI drive users - I really can't continue +any more to answer them all. So, if your drive/interface information sheets +mention "IDE" (primary, secondary, tertiary, quaternary) and the DOS driver +invoking line within your CONFIG.SYS is using an address below 0x230: +DON'T ROB MY LAST NERVE - jumper your interface to address 0x170 and IRQ 15 +(that is the "secondary IDE" configuration), set your drive to "master" and +use ide-cd as your driver. If you do not have a second IDE hard disk, use the +LILO commands + hdb=noprobe hdc=cdrom +and get lucky. +To make it fully clear to you: if you mail me about IDE/ATAPI drive problems, +my answer is above, and I simply will discard your mail, hoping to stop the +flood and to find time to lead my 12-years old son towards happy computing. + +The driver is able to drive the whole family of "traditional" AT-style (that +is NOT the new "Enhanced IDE" or "ATAPI" drive standard) Matsushita, +Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The +well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563. +CR-574 is an IDE/ATAPI drive. + +The Longshine LCS-7260 is a double-speed drive which uses the "old" +Matsushita command set. It is supported - with help by Serge Robyns. + +There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563 +with a special controller board. This drive is supported (the interface is +of the "LaserMate" type), and it is possibly the best buy today (cheaper than +an internal drive, and you can use it as an internal, too - f.e. plug it into +a soundcard). + +CreativeLabs has a new drive "CD200" and a similar drive "CD200F". The latter +is made by Funai and sometimes named "E2550UA". Support is under construction + - CD200F should work, CD200 is still giving problems. +Drive detection and playing audio should work. I need qualified feedback +about the bugs within the data functions or a drive (I never saw a CD200). + +The quad-speed TEAC CD-55A drive is supported, but still does not reach "full +speed". The data rate already reaches 500 kB/sec if you set SBP_BUFFER_FRAMES +to 64 (it is not recommended to do that for normal "file access" usage, but it +can speed up things a lot if you use something like "dd" to read from the +drive; I use it for verifying self-written CDs this way). +The drive itself is able to deliver 600 kB/sec, so this has to get a point of +work; with the normal setup, the performance currently is not even as good as +double-speed. + +This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives, +and again: this driver is in no way usable for any IDE/ATAPI drive. If you +think your drive should work and it doesn't: send me the DOS driver for your +beast (gzipped + uuencoded) and your CONFIG.SYS if you want to ask me for help, +and include an original log message excerpt, and try to give all information +a complete idiot needs to understand your hassle already with your first +mail. And if you want to say "as I have mailed you before", be sure that I +don't remember your "case" by such remarks; at the moment, I have some +hundreds open correspondences about Linux CDROM questions (hope to reduce if +the IDE/ATAPI user questions disappear). + + +This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy, +SoundFX, Mozart, ...) and with the "no-sound" cards (Panasonic CI-101P, +LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...). + +It finally works now with the "configurable" interface "Sequoia S-1000", too, +which is found on the Spea Media FX and Ensonic Soundscape sound cards. You +have to specify the type "SBPRO 2" and the true CDROM port address with it, +not the "configuration port" address. + +If you have a sound card which needs a "configuration driver" instead of +jumpers for interface types and addresses (like Mozart cards) - those +drivers get invoked before the DOS CDROM driver in your CONFIG.SYS, typical +names are "cdsetup.sys" and "mztinit.sys" -, let the sound driver do the +CDROM port configuration (the leading comments within +linux/drivers/sound/mad16.c are just for you!). Hannu Savolainen's mad16.c +code is able to set up my Mozart card - I simply had to add + #define MAD16_CONF 0x06 + #define MAD16_CDSEL 0x03 +to configure the CDROM interface for type "Panasonic" (LaserMate) and address +0x340. + +The interface type has to get configured in /usr/include/linux/sbpcd.h, +because the register layout is different between the "SoundBlaster" and the +"LaserMate" type. + +I got a report that the TEAC interface card "I/F E117098" is of type +"SoundBlaster" (i.e. you have to set SBPRO to 1) even with the addresses +0x300 and above. This is unusual, and it can't get covered by the auto +probing scheme. +If auto-probing found the drive, the address is correct. The reported type +may be wrong. A "mount" will give success only if the interface type is set +right. Playing audio should work with a wrong set interface type, too. + +With some TEAC and some CD200 drives I have seen interface cards which seem +to lack the "drive select" lines; always drive 0 gets addressed. To avoid +"mirror drives" (four drives detected where you only have one) with such +interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0 (if +possible). + + +Up to 4 drives per interface card, and up to 4 interface cards are supported. +All supported drive families can be mixed, but the CR-521 drives are +hard-wired to drive ID 0. The drives have to use different drive IDs, and each +drive has to get a unique minor number (0...3), corresponding indirectly to +its drive ID. +The drive IDs may be selected freely from 0 to 3 - they do not have to be in +consecutive order. + +As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible +to change old drives to any ID, too. He writes in this sense: + "In order to be able to use more than one single speed drive + (they do not have the ID jumpers) you must add a DIP switch + and two resistors. The pads are already on the board next to + the power connector. You will see the silkscreen for the + switch if you remove the top cover. + 1 2 3 4 + ID 0 = x F F x O = "on" + ID 1 = x O F x F = "off" + ID 2 = x F O x x = "don't care" + ID 3 = x O O x + Next to the switch are the positions for R76 (7k) and R78 + (12k). I had to play around with the resistor values - ID 3 + did not work with other values. If the values are not good, + ID 3 behaves like ID 0." + +To use more than 4 drives, you simply need a second controller card at a +different address and a second cable. + +The driver supports reading of data from the CD and playing of audio tracks. +The audio part should run with WorkMan, xcdplayer, with the "non-X11" products +CDplayer and WorkBone - tell me if it is not compatible with other software. + +With the CR-562 and CR-563 drives, the reading of audio frames is possible. +This is implemented by an IOCTL function which reads READ_AUDIO frames of +2352 bytes at once (configurable with the "READ_AUDIO" define, default is 0). +Reading the same frame a second time gives different data; the frame data +start at a different position, but all read bytes are valid, and we always +read 98 consecutive chunks (of 24 Bytes) as a frame. Reading more than 1 frame +at once possibly misses some chunks at each frame boundary. This lack has to +get corrected by external, "higher level" software which reads the same frame +again and tries to find and eliminate overlapping chunks (24-byte-pieces). + +The transfer rate with reading audio (1-frame-pieces) currently is very slow. +This can be better reading bigger chunks, but the "missing" chunks possibly +occur at the beginning of each single frame. +The software interface possibly may change a bit the day the SCSI driver +supports it too. + +With all but the CR-52x drives, MultiSession is supported. +Photo CDs work (the "old" drives like CR-521 can access only the first +session of a photoCD). +At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to +convert photo CD image files and Gerd Knorr's viewing utility. + +The transfer rate will reach 150 kB/sec with CR-52x drives, 300 kB/sec with +CR-56x drives, and currently not more than 500 kB/sec (usually less than +250 kB/sec) with the TEAC quad speed drives. +XA (PhotoCD) disks with "old" drives give only 50 kB/sec. + +This release consists of +- this README file +- the driver file linux/drivers/block/sbpcd.c +- the stub files linux/drivers/block/sbpcd[234].c +- the header file linux/include/linux/sbpcd.h. + + +To install: +----------- + +1. Setup your hardware parameters. Though the driver does "auto-probing" at a + lot of (not all possible!) addresses, this step is recommended for + every-day use. You should let sbpcd auto-probe once and use the reported + address if a drive got found. The reported type may be incorrect; it is + correct if you can mount a data CD. There is no choice for you with the + type; only one is the right, the others are deadly wrong. + + a. Go into /usr/src/linux/include/linux/sbpcd.h and configure it for your + hardware (near the beginning): + a1. Set it up for the appropriate type of interface board. + "Original" CreativeLabs sound cards need "SBPRO 1". + Most "compatible" sound cards (almost all "non-CreativeLabs" cards) + need "SBPRO 0". + The "no-sound" board from OmniCd needs the "SBPRO 1" setup. + All other "no-sound" boards need the "SBPRO 0" setup. + Possibly some TEAC "no-sound" boards need the "SBPRO 1" setup. + The Spea Media FX sound card needs "SBPRO 2". + sbpcd.c holds some examples in its auto-probe list. + If you configure "SBPRO" wrong, the playing of audio CDs will work, + but you will not be able to mount a data CD. + a2. Tell the address of your CDROM_PORT (not of the sound port). + a3. If 4 drives get found, but you have only one, set MAX_DRIVES to 1. + a4. Set DISTRIBUTION to 0. + b. Additionally for 2.a1 and 2.a2, the setup may be done during + boot time (via the "kernel command line" or "LILO option"): + sbpcd=0x230,SoundBlaster + or + sbpcd=0x320,LaserMate + or + sbpcd=0x330,SPEA + This is especially useful if you install a fresh distribution. + If the second parameter is a number, it gets taken as the type + setting; 0 is "LaserMate", 1 is "SoundBlaster". + So, for example + sbpcd=0x230,1 + is equivalent to + sbpcd=0x230,SoundBlaster + +2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita + CD-ROM support and for ISO9660 FileSystem support. If you do not have a + second, third, or fourth controller installed, do not say "y" to the + secondary Matsushita CD-ROM questions. + +3. Then do a "make dep", then make the kernel image ("make zlilo" or else). + +4. Make the device file(s). This step usually already has been done by the + MAKEDEV script. + The driver uses MAJOR 25, so, if necessary, do + mknod /dev/sbpcd b 25 0 (if you have only one drive) + and/or + mknod /dev/sbpcd0 b 25 0 + mknod /dev/sbpcd1 b 25 1 + mknod /dev/sbpcd2 b 25 2 + mknod /dev/sbpcd3 b 25 3 + to make the node(s). + + The "first found" drive gets MINOR 0 (regardless to its jumpered ID), the + "next found" (at the same cable) gets MINOR 1, ... + + For a second interface board, you have to make nodes like + mknod /dev/sbpcd4 b 26 0 + mknod /dev/sbpcd5 b 26 1 + and so on. Use the MAJORs 26, 27, 28. + + If you further make a link like + ln -s sbpcd /dev/cdrom + you can use the name /dev/cdrom, too. + +5. Reboot with the new kernel. + +You should now be able to do + mkdir /CD +and + mount -rt iso9660 /dev/sbpcd /CD +or + mount -rt iso9660 -o block=2048 /dev/sbpcd /CD +and see the contents of your CD in the /CD directory. +To use audio CDs, a mounting is not recommended (and it would fail if the +first track is not a data track). + + +Using sbpcd as a "loadable module": +----------------------------------- + +If you do NOT select "Matsushita/Panasonic CDROM driver support" during the +"make config" of your kernel, you can build the "loadable module" sbpcd.o. +Read /usr/src/linux/README.modules on this. + +If sbpcd gets used as a module, the support of more than one interface +card (i.e. drives 4...15) is disabled. + +You can specify interface address and type with the "insmod" command like: + # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x340,0 +or + # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1 +where the last number represents the SBPRO setting (no strings allowed here). + + +Things of interest: +------------------- + +The driver is configured to try the LaserMate type of interface at I/O port +0x0340 first. If this is not appropriate, sbpcd.h should get changed +(you will find the right place - just at the beginning). + +No DMA and no IRQ is used. + +To reduce or increase the amount of kernel messages, edit sbpcd.c and play +with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug"). +Don't forget to reflect what you do; enabling all DBG_xxx switches at once +may crash your system. + +The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to +specify "block=2048" as a mount option. Doing this will disable the direct +execution of a binary from the CD; you have to copy it to a device with the +standard BLOCK_SIZE (1024) before. So, do not use this if your system is +directly "running from the CDROM" (like some of YGGDRASIL's installation +variants). There are CDs on the market (like the german "unifix" Linux +distribution) which MUST get handled with a block_size of 1024. Generally, +one can say all the CDs which hold files of the name YMTRANS.TBL are defective; +do not use block=2048 with those. + +Within sbpcd.h, you will find some "#define"s (f.e. EJECT and JUKEBOX). With +that, you can configure the driver for some special things. +You can use the appended program "cdtester" to set the auto-eject feature +during runtime. Jeff Tranter's "eject" utility can do this, too (and more) +for you. + +There is an ioctl CDROMMULTISESSION to obtain with a user program if +the CD is an XA disk and - if it is - where the last session starts. The +"cdtester" program illustrates how to call it. + + +Auto-probing at boot time: +-------------------------- + +The driver does auto-probing at many well-known interface card addresses, +but not all: +Some probings can cause a hang if an NE2000 ethernet card gets touched, because +SBPCD's auto-probing happens before the initialization of the net drivers. +Those "hazardous" addresses are excluded from auto-probing; the "kernel +command line" feature has to be used during installation if you have your +drive at those addresses. The "module" version is allowed to probe at those +addresses, too. + +The auto-probing looks first at the configured address resp. the address +submitted by the kernel command line. With this, it is possible to use this +driver within installation boot floppies, and for any non-standard address, +too. + +Auto-probing will make an assumption about the interface type ("SBPRO" or not), +based upon the address. That assumption may be wrong (initialization will be +o.k., but you will get I/O errors during mount). In that case, use the "kernel +command line" feature and specify address & type at boot time to find out the +right setup. + +For every-day use, address and type should get configured within sbpcd.h. That +will stop the auto-probing due to success with the first try. + +The kernel command "sbpcd=0" suppresses each auto-probing and causes +the driver not to find any drive; it is meant for people who love sbpcd +so much that they do not want to miss it, even if they miss the drives. ;-) + +If you configure "#define CDROM_PORT 0" in sbpcd.h, the auto-probing is +initially disabled and needs an explicit kernel command to get activated. +Once activated, it does not stop before success or end-of-list. This may be +useful within "universal" CDROM installation boot floppies (but using the +loadable module would be better because it allows an "extended" auto-probing +without fearing NE2000 cards). + +To shorten the auto-probing list to a single entry, set DISTRIBUTION 0 within +sbpcd.h. + + +Setting up address and interface type: +-------------------------------------- + +If your I/O port address is not 0x340, you have to look for the #defines near +the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and +change CDROM_PORT to the address of your CDROM I/O port. + +Almost all of the "SoundBlaster compatible" cards behave like the no-sound +interfaces, i.e. need SBPRO 0! + +With "original" SB Pro cards, an initial setting of CD_volume through the +sound cards MIXER register gets done. +If you are using a "compatible" sound card of types "LaserMate" or "SPEA", +you can set SOUND_BASE (in sbpcd.h) to get it done with your card, too... + + +Using audio CDs: +---------------- + +Workman, WorkBone, xcdplayer, cdplayer and the nice little tool "cdplay" (see +README.aztcd from the Aztech driver package) should work. + +The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer wants +"/dev/rsr0", workman loves "/dev/sr0" or "/dev/cdrom" - so, do the appropriate +links for using them without the need of supplying parameters. + + +Copying audio tracks: +--------------------- + +The following program will copy track 1 (or a piece of it) from an audio CD +into the file "track01": + +/*=================== begin program ========================================*/ +/* + * read an audio track from a CD + * + * (c) 1994 Eberhard Moenkeberg + * 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 +#include +#include + +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 + * published under the GPL + * + * made under heavy use of the "Tiny Audio CD Player" + * from Werner Zimmermann + * (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 +#include +#include +#include +#include + +#ifdef AZT_PRIVATE_IOCTLS +#include +#endif AZT_PRIVATE_IOCTLS +#ifdef SBP_PRIVATE_IOCTLS +#include +#include +#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 \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_trk0last) 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_trk0last) 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 ((ihdr.cdth_trk1)) + printf("invalid track number.\n"); + else + printf("TocEntry %02d: adr=%01X ctrl=%01X msf=%02d:%02d:%02d mode=%02X\n", + TocEntry[i].cdte_track, + TocEntry[i].cdte_adr, + TocEntry[i].cdte_ctrl, + TocEntry[i].cdte_addr.msf.minute, + TocEntry[i].cdte_addr.msf.second, + TocEntry[i].cdte_addr.msf.frame, + TocEntry[i].cdte_datamode); + break; + case 'A': /* read audio data into file */ + printf("Address (min:sec:frm) ? "); + scanf("%d:%d:%d",&i1,&i2,&i3); + read_audio.addr.msf.minute=i1; + read_audio.addr.msf.second=i2; + read_audio.addr.msf.frame=i3; + read_audio.addr_format=CDROM_MSF; + printf("# of frames ? "); + scanf("%d",&i1); + read_audio.nframes=i1; + k=read_audio.nframes*CD_FRAMESIZE_RAW; + read_audio.buf=malloc(k); + if (read_audio.buf==NULL) + { + printf("can't malloc %d bytes.\n",k); + break; + } + sprintf(filename,"audio_%02d%02d%02d_%02d.%02d\0", + read_audio.addr.msf.minute, + read_audio.addr.msf.second, + read_audio.addr.msf.frame, + read_audio.nframes, + ++sequence); + datafile=creat(filename, 0755); + if (datafile<0) + { + printf("can't open datafile %s.\n",filename); + break; + } + rc=ioctl(drive,CDROMREADAUDIO,&read_audio); + if (rc!=0) + { + printf("CDROMREADAUDIO: rc=%d.\n",rc); + } + else + { + rc=write(datafile,&read_audio.buf,k); + if (rc!=k) printf("datafile I/O error (%d).\n",rc); + } + close(datafile); + break; + case 'X': /* set EJECT_SW (0: disable, 1: enable auto-ejecting) */ + scanf("%d",&i); + rc=ioctl(drive,CDROMEJECT_SW,i); + if (rc!=0) + printf("CDROMEJECT_SW: rc=%d.\n",rc); + else + printf("EJECT_SW set to %d\n",i); + break; + case 'M': /* get the multisession redirection info */ + ms_info.addr_format=CDROM_LBA; + rc=ioctl(drive,CDROMMULTISESSION,&ms_info); + if (rc!=0) + { + printf("CDROMMULTISESSION(lba): rc=%d.\n",rc); + } + else + { + if (ms_info.xa_flag) printf("MultiSession offset (lba): %d (0x%06X)\n",ms_info.addr.lba,ms_info.addr.lba); + else + { + printf("this CD is not an XA disk.\n"); + break; + } + } + ms_info.addr_format=CDROM_MSF; + rc=ioctl(drive,CDROMMULTISESSION,&ms_info); + if (rc!=0) + { + printf("CDROMMULTISESSION(msf): rc=%d.\n",rc); + } + else + { + if (ms_info.xa_flag) + printf("MultiSession offset (msf): %02d:%02d:%02d (0x%02X%02X%02X)\n", + ms_info.addr.msf.minute, + ms_info.addr.msf.second, + ms_info.addr.msf.frame, + ms_info.addr.msf.minute, + ms_info.addr.msf.second, + ms_info.addr.msf.frame); + else printf("this CD is not an XA disk.\n"); + } + break; +#ifdef SBP_PRIVATE_IOCTLS + case 'Y': /* set the driver's message level */ +#if 0 /* not implemented yet */ + printf("enter switch name (f.e. DBG_CMD): "); + scanf("%s",&dbg_switch); + j=get_dbg_num(dbg_switch); +#else + printf("enter DDIOCSDBG switch number: "); + scanf("%d",&j); +#endif + printf("enter 0 for \"off\", 1 for \"on\": "); + scanf("%d",&i); + if (i==0) j|=0x80; + printf("calling \"ioctl(drive,DDIOCSDBG,%d)\"\n",j); + rc=ioctl(drive,DDIOCSDBG,j); + printf("DDIOCSDBG: rc=%d.\n",rc); + break; + case 'Z': /* set the audio buffer size */ + printf("# frames wanted: ? "); + scanf("%d",&j); + rc=ioctl(drive,CDROMAUDIOBUFSIZ,j); + printf("%d frames granted.\n",rc); + break; +#endif SBP_PRIVATE_IOCTLS + default: + printf("unknown command: \"%s\".\n",command); + break; + } + } +} +/*==========================================================================*/ + diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/sjcd linux/Documentation/cdrom/sjcd --- v1.3.35/linux/Documentation/cdrom/sjcd Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/sjcd Mon Oct 16 15:23:25 1995 @@ -0,0 +1,80 @@ + -- README.sjcd + 80% of the work takes 20% of the time, + 20% of the work takes 80% of the time... + (Murphy law) + + Once started, training can not be stopped... + (StarWars) + +This is the README for the sjcd cdrom driver, version 1.5. + +This file is meant as a tips & tricks edge for the usage of the SANYO CDR-H94A +cdrom drive. It will grow as the questions arise. ;-) +Since the drive often comes with an ISP16 soundcard, which can be used +as cdrom interface, this is also the place for ISP16 related issues. + +The driver should work with any SoundBlaster/Panasonic style CDROM interface, +including the "soft configurable" MediaMagic sound card. +To make this sound card (and others like "Mozart") working, it has to get +"configured" by software. +The suggestion to configure the ISP16 soundcard by booting DOS and +a warm reboot to boot Linux somehow doesn't work, at least not +on Eric's machine (IPC P90), with the version of the ISP16 +card he has (there appear to be at least two versions: Eric's card with +no jumpered IDE support and OPTi 82C928 chip, and Vadim's version +with a jumper to enable IDE support, probably with a OPTi 82C929 chip). +Therefore detection and configuration of the ISP16 interfaces is included +in the driver. +If we should support any other interfaces (which cannot be configured +through DOS) or if there are any more ISP16 types, please let us +know (maarel@marin.nl) and we'll see. + +Otherwise, you should boot DOS once (and by this, run the "configuration driver") +and then switch to Linux by use of CTRL-ALT-DEL. Each use of the RESET +button or the power switch makes this procedure necessary again. +If no ISP16 is detected, there's no harm done; a card configured trough DOS +may still work as expected. + +As of version 1.4 sound through the speakers is supported; only for MSS-mode +and no volume controle yet. + +PAUSE and STOP ioctl commands don't seem to work yet. + +ISP16 configuration routines reside at Vadim's server + ftp.rbrf.ru:/linux/mediamagic/ +and at Eberhard's mirror + ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/ + +Leo Spiekman's configuration routines for the ISP-16 card can get found at + dutette.et.tudelft.nl:/pub/linux/ +and at Eberhard's mirror + ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/ + +Eric van der Maarel's routines are included in sjcd.c. +This, and any related stuff may be found by anonymous ftp at + ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/ + +The device major for sjcd is 18, and minor is 0. Create a block special +file in your /dev directory (e.g., /dev/sjcd) with these numbers. +(For those who don't know, being root and doing the following should do the trick: + mknod -m 644 /dev/sjcd b 18 0 +and mount the cdrom by /dev/sjcd). + +The default configuration parameters are: + base address 0x340 + no irq + no dma +As of version 1.2, setting base address, irq and dma at boot time is supported +through the use of command line options: type at the "boot:" prompt: + linux sjcd=,, +(where your kernel is assumed to be called by saying "linux" to +the boot manager). + +If something is wrong, e-mail to vadim@rbrf.ru + or vadim@ipsun.ras.ru + or model@cecmow.enet.dec.com + +It happens sometimes that Vadim is not reachable by mail. For these +instances, Eric van der Maarel (maarel@marin.nl) will help, too. + + Vadim V. Model, Eric van der Maarel, Eberhard Moenkeberg diff -u --recursive --new-file v1.3.35/linux/Documentation/cdrom/sonycd535 linux/Documentation/cdrom/sonycd535 --- v1.3.35/linux/Documentation/cdrom/sonycd535 Thu Jan 1 02:00:00 1970 +++ linux/Documentation/cdrom/sonycd535 Sat Jul 1 19:05:58 1995 @@ -0,0 +1,121 @@ + README FOR LINUX SONY CDU-535/531 DRIVER + ======================================== + +This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux. +I do not think I have the documentation to add features like DMA support +so if anyone else wants to pursue it or help me with it, please do. +(I need to see what was done for the CDU-31A driver -- perhaps I can +steal some of that code.) + +This is a Linux device driver for the Sony CDU-535 CDROM drive. This is +one of the older Sony drives with its own interface card (Sony bus). +The DOS driver for this drive is named SONY_CDU.SYS - when you boot DOS +your drive should be identified as a SONY CDU-535. The driver works +with a CDU-531 also. One user reported that the driver worked on drives +OEM'ed by Procomm, drive and interface board were labelled Procomm. + +The Linux driver is based on Corey Minyard's sonycd 0.3 driver for +the CDU-31A. Ron Jeppesen just changed the commands that were sent +to the drive to correspond to the CDU-535 commands and registers. +There were enough changes to let bugs creep in but it seems to be stable. +Ron was able to tar an entire CDROM (should read all blocks) and built +ghostview and xfig off Walnut Creek's X11R5/GNU CDROM. xcdplayer and +workman work with the driver. Others have used the driver without +problems except those dealing with wait loops (fixed in third release). +Like Minyard's original driver this one uses a polled interface (this +is also the default setup for the DOS driver). It has not been tried +with interrupts or DMA enabled on the board. + +REQUIREMENTS +============ + + - Sony CDU-535 drive, preferably without interrupts and DMA + enabled on the card. + + - Drive must be set up as unit 1. Only the first unit will be + recognized + + - you must enter your interface address into + /usr/src/linux/include/linux/sonycd535.h and build the + appropriate kernel or use the "kernel command line" parameter + sonycd535=0x320 + with the correct interface address. + +NOTES: +====== + +1) The drive MUST be turned on when booting or it will not be recognized! + (but see comments on modularized version below) + +2) when the cdrom device is opened the eject button is disabled to keep the + user from ejecting a mounted disk and replacing it with another. + Unfortunately xcdplayer and workman also open the cdrom device so you + have to use the eject button in the software. Keep this in mind if your + cdrom player refuses to give up its disk -- exit workman or xcdplayer, or + umount the drive if it has been mounted. + +THANKS +====== + +Many thanks to Ron Jeppesen (ronj.an@site007.saic.com) for getting +this project off the ground. He wrote the initial release +and the first two patches to this driver (0.1, 0.2, and 0.3). +Thanks also to Eberhard Moenkeberg (emoenke@gwdg.de) for prodding +me to place this code into the mainstream Linux source tree +(as of Linux version 1.1.91), as well as some patches to make +it a better device citizen. Further thanks to "S. Joel Katz" + for his MODULE patches (see details below), +Porfiri Claudio for patches +to make the driver work with the older CDU-510/515 series, and +Heiko Eissfeldt 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 .) + + 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=
+ + And to remove it: + +rmmod sony535 + + The code checks to see if MODULE is defined and behaves as it used +to if MODULE is not defined. That means your patched file should behave +exactly as it used to if compiled into the kernel. + + I have an external drive, and I usually leave it powered off. I used +to have to reboot if I needed to use the CDROM drive. Now I don't. + + Even if you have an internal drive, why waste the 268K of memory +(unswappable) that the driver uses if you use your CD-ROM drive infrequently? + + This driver will not install (whether compiled in or loaded as a +module) if the CDROM drive is not available during its initialization. This +means that you can have the driver compiled into the kernel and still load +the module later (assuming the driver doesn't install itself during +power-on). This only wastes 12K when you boot with the CDROM drive off. + + This is what I usually do; I leave the driver compiled into the +kernel, but load it as a module if I powered the system up with the drive +off and then later decided to use the CDROM drive. + + Since the driver only uses a single page to point to the chunks, +attempting to set the buffer cache to more than 2 Megabytes would be very +bad; don't do that. diff -u --recursive --new-file v1.3.35/linux/MAGIC linux/MAGIC --- v1.3.35/linux/MAGIC Mon Oct 16 18:38:21 1995 +++ linux/MAGIC Fri Oct 20 10:10:39 1995 @@ -36,39 +36,62 @@ Theodore Ts'o 31-Mar-94 +The magic table is current to Linux 1.3.35. +The ioctl table is current to Linux 1.3.35. +For a complete list of kernel ioctl's, look for 'ioctl-list.X.Y.ZZ' on +an ftp site, where 'X.Y.ZZ' is the kernel version for the ioctl list. + + Michael Chastain + + 17-Oct-95 + + + + Magic Name Number Structure File =========================================================================== +CYCLADES_MAGIC 0x4359 struct cyclades_port include/linux/cyclades.h FASYNC_MAGIC 0x4601 struct fasync_struct include/linux/fs.h PTY_MAGIC 0x5001 struct pty_struct drivers/char/pty.c -PPP_MAGIC 0x5002 struct ppp_struct include/linux/ppp.h +PPP_MAGIC 0x5002 struct ppp_struct include/linux/if_ppp.h TTY_MAGIC 0x5401 struct tty_struct include/linux/tty.h TTY_DRIVER_MAGIC 0x5402 struct tty_driver include/linux/tty_driver.h TTY_LDISC_MAGIC 0x5403 struct tty_ldisc include/linux/tty_ldisc.h SERIAL_MAGIC 0x5301 struct async_struct include/linux/serial.h SLIP_MAGIC 0x5302 struct slip drivers/net/slip.h +SCC_MAGIC 0x8530 struct scc_channel include/linux/scc.h -Ioctl Include File Comments -======================================================== -0x00 fd.h -0x01 random.h subcodes starting at 0x080000 -0x03 hdreg.h -0x06 lp.h -0x12 fs.h -'C' soundcard.h -'K' kd.h -'M' soundcard.h -'P' soundcard.h -'Q' soundcard.h -'S' cdrom.h -'S' fs.h subcodes from 0x80, no conflict -'T' soundcard.h conflicts with termios.h -'T' termios.h conflicts with soundcard.h -'T' ppp.h subcodes from 0x90, no conflict -'V' vt.h -'f' ext2_fs.h -'m' mtio.h conflicts with soundcard.h -'m' soundcard.h conflicts with mtio.h -'s' pcsp.h -'v' ext2_fs.h -0x89 sockios.h + +Ioctl Include File Comments +======================================================== +0x00 linux/fs.h only FIBMAP, FIGETBSZ +0x00 linux/random.h codes in 0x010800NN +0x02 linux/fd.h +0x03 linux/hdreg.h +0x04 linux/umsdos_fs.h +0x06 linux/lp.h +0x12 linux/fs.h +0x20 linux/cm206.h +'C' linux/soundcard.h +'K' linux/kd.h +'M' linux/soundcard.h +'P' linux/soundcard.h +'Q' linux/soundcard.h +'S' linux/cdrom.h conflict! +'S' linux/scsi.h conflict! +'T' linux/soundcard.h conflict! +'T' linux/scc.h conflict! +'T' asm/termios.h conflict! +'V' linux/vt.h +'Y' linux/cyclades.h codes in 0x004359NN +'f' linux/ext2_fs.h +'m' linux/mtio.h conflict! +'m' linux/soundcard.h conflict! +'s' linux/cdk.h +'t' linux/if_ppp.h +'u' linux/smb_fs.h +'u' linux/smb_fs.h +'v' linux/ext2_fs.h +0x89 asm/socket.h no conflict +0x89 linux/sockios.h no conflict diff -u --recursive --new-file v1.3.35/linux/Makefile linux/Makefile --- v1.3.35/linux/Makefile Mon Oct 16 18:38:21 1995 +++ linux/Makefile Sat Oct 21 17:49:54 1995 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 3 -SUBLEVEL = 35 +SUBLEVEL = 36 ARCH = i386 @@ -94,7 +94,11 @@ LIBS =$(TOPDIR)/lib/lib.a SUBDIRS =kernel drivers mm fs net ipc lib -ifdef CONFIG_SCSI +ifdef CONFIG_CD_NO_IDESCSI +DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a +endif + +ifeq ($(CONFIG_SCSI),y) DRIVERS := $(DRIVERS) drivers/scsi/scsi.a endif diff -u --recursive --new-file v1.3.35/linux/arch/alpha/config.in linux/arch/alpha/config.in --- v1.3.35/linux/arch/alpha/config.in Mon Oct 16 18:38:21 1995 +++ linux/arch/alpha/config.in Sun Oct 22 22:17:41 1995 @@ -13,23 +13,6 @@ define_bool CONFIG_NATIVE y fi -tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD y -bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y -if [ "$CONFIG_ST506" = "y" ]; then - comment 'Please see drivers/block/README.ide for help/info on IDE drives' - bool ' Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD n - if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then - bool ' Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n - else - bool ' Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y - fi - if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then - bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n - fi -fi - -bool 'XT harddisk support' CONFIG_BLK_DEV_XD n -bool 'Networking support' CONFIG_NET y choice 'Alpha system type' \ "Jensen CONFIG_ALPHA_JENSEN \ Noname CONFIG_ALPHA_NONAME \ @@ -47,295 +30,72 @@ if [ "$CONFIG_ALPHA_CABRIOLET" = "y" \ -o "$CONFIG_ALPHA_EB64" = "y" -o "$CONFIG_ALPHA_EB64P" = "y" ] then - bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM n + bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM define_bool CONFIG_PCI y define_bool CONFIG_ALPHA_APECS y fi if [ "$CONFIG_PCI" = "y" ]; then - bool ' PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE n - if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then - bool ' PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON y - fi + bool ' PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE fi -bool 'System V IPC' CONFIG_SYSVIPC y -tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +mainmenu_option next_comment comment 'Loadable module support' -bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n +bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + +source drivers/block/Config.in if [ "$CONFIG_NET" = "y" ]; then -mainmenu_option next_comment -comment 'Networking options' -bool 'TCP/IP networking' CONFIG_INET y -if [ "$CONFIG_INET" = "y" ]; then -bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n -bool 'IP: multicasting' CONFIG_IP_MULTICAST n -bool 'IP: firewalling' CONFIG_IP_FIREWALL n -bool 'IP: accounting' CONFIG_IP_ACCT n -tristate 'IP: tunneling' CONFIG_NET_IPIP n -if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then - bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y - bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n -fi -if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_MULTICAST" = "y" -a "$CONFIG_NET_IPIP" = "y" ]; then - bool 'IP: multicast routing(in progress)' CONFIG_IP_MROUTE n -fi -comment '(it is safe to leave these untouched)' -bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n -tristate 'IP: Reverse ARP' CONFIG_INET_RARP n -bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y -bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n -bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y -bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE y -fi -bool 'The IPX protocol' CONFIG_IPX n -bool 'Appletalk DDP' CONFIG_ATALK n -bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n -if [ "$CONFIG_AX25" = "y" ]; then - bool 'Amateur Radio NET/ROM' CONFIG_NETROM n -fi + source net/Config.in fi mainmenu_option next_comment comment 'SCSI support' -tristate 'SCSI support' CONFIG_SCSI y - -if [ "$CONFIG_SCSI" = "n" ]; then - -comment 'Skipping SCSI configuration options...' - -else +tristate 'SCSI support' CONFIG_SCSI -comment 'SCSI support type (disk, tape, CDrom)' - -dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD y $CONFIG_SCSI -dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST n $CONFIG_SCSI -dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR y $CONFIG_SCSI -dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG n $CONFIG_SCSI - -comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' - -bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n - -mainmenu_option next_comment -comment 'SCSI low-level drivers' - -dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n $CONFIG_SCSI -dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n $CONFIG_SCSI -dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n $CONFIG_SCSI -dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX n $CONFIG_SCSI -dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n $CONFIG_SCSI -dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA n $CONFIG_SCSI -dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO n $CONFIG_SCSI -dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n $CONFIG_SCSI -dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n $CONFIG_SCSI -bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n -if [ "$CONFIG_PCI" = "y" ]; then - dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx y $CONFIG_SCSI -fi -dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n $CONFIG_SCSI -bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n -dep_tristate 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n $CONFIG_SCSI -dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n $CONFIG_SCSI -bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n -dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n $CONFIG_SCSI -dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST n $CONFIG_SCSI -dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n $CONFIG_SCSI -#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n $CONFIG_SCSI +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in fi - if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' -mainmenu_option next_comment -comment 'Network device support' - -bool 'Network device support' CONFIG_NETDEVICES y -if [ "$CONFIG_NETDEVICES" = "n" ]; then - -comment 'Skipping network driver configuration options...' - -else -tristate 'Dummy net driver support' CONFIG_DUMMY n -tristate 'SLIP (serial line) support' CONFIG_SLIP n -if [ "$CONFIG_SLIP" != "n" ]; then - bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y -fi -tristate 'PPP (point-to-point) support' CONFIG_PPP n -if [ ! "$CONFIG_PPP" = "n" ]; then -comment 'CCP compressors for PPP are only built as modules.' -fi -if [ "$CONFIG_AX25" = "y" ]; then - bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC y -else - bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC n -fi -tristate 'PLIP (parallel port) support' CONFIG_PLIP n -tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n -bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n -bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n -if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then - tristate 'WD80*3 support' CONFIG_WD80x3 n - tristate 'SMC Ultra support' CONFIG_ULTRA n -fi -bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n -bool '3COM cards' CONFIG_NET_VENDOR_3COM n -if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then - tristate '3c501 support' CONFIG_EL1 n - tristate '3c503 support' CONFIG_EL2 n - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - tristate '3c505 support' CONFIG_ELPLUS n - tristate '3c507 support' CONFIG_EL16 n - fi - tristate '3c509/3c579 support' CONFIG_EL3 y -fi -bool 'Other ISA cards' CONFIG_NET_ISA n -if [ "$CONFIG_NET_ISA" = "y" ]; then - tristate 'Cabletron E21xx support' CONFIG_E2100 n - tristate 'DEPCA support' CONFIG_DEPCA y - tristate 'EtherWorks 3 support' CONFIG_EWRK3 n - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - bool 'SEEQ8005 support' CONFIG_SEEQ8005 n - tristate 'AT1700 support' CONFIG_AT1700 n - tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n - tristate 'EtherExpress support' CONFIG_EEXPRESS n - bool 'NI5210 support' CONFIG_NI52 n - bool 'NI6510 support' CONFIG_NI65 n - if [ "$CONFIG_AX25" = "y" ]; then - bool 'Ottawa PI and PI/2 support' CONFIG_PI y - fi - tristate 'WaveLAN support' CONFIG_WAVELAN n - fi - tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n - tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n - tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 n - tristate 'NE2000/NE1000 support' CONFIG_NE2000 n - bool 'SK_G16 support' CONFIG_SK_G16 n -fi -bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA y -if [ "$CONFIG_NET_EISA" = "y" ]; then - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - tristate 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n - fi - tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n - tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5 y -# tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP n -# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n -# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n - bool 'Zenith Z-Note support' CONFIG_ZNET n -fi -bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n -if [ "$CONFIG_NET_POCKET" = "y" ]; then - bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n - tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n - tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n -# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n -# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n -# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n -fi -bool 'Token Ring driver support' CONFIG_TR n -if [ "$CONFIG_TR" = "y" ]; then - tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR y -fi -tristate 'Arcnet support' CONFIG_ARCNET n -fi -fi - -mainmenu_option next_comment -comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' - -bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI n -if [ "$CONFIG_CD_NO_IDESCSI" = "y" ]; then - tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A n - tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD n - tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX n - tristate 'Matsushita/Panasonic CDROM support' CONFIG_SBPCD n - if [ "$CONFIG_SBPCD" = "y" ]; then - bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n - if [ "$CONFIG_SBPCD2" = "y" ]; then - bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n - if [ "$CONFIG_SBPCD3" = "y" ]; then - bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n - fi - fi + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in fi - tristate 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n - tristate 'Sony CDU535 CDROM support' CONFIG_CDU535 n - tristate 'Goldstar R420 CDROM support' CONFIG_GSCD n - tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206 n - tristate 'Experimental Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD n - bool 'Experimental Sanyo H94A CDROM support' CONFIG_SJCD n fi mainmenu_option next_comment -comment 'Filesystems' - -tristate 'Standard (minix) fs support' CONFIG_MINIX_FS y -bool 'Extended fs support' CONFIG_EXT_FS n -bool 'Second extended fs support' CONFIG_EXT2_FS y -tristate 'xiafs filesystem support' CONFIG_XIA_FS n -tristate 'msdos fs support' CONFIG_MSDOS_FS y -if [ "$CONFIG_MSDOS_FS" != "n" ]; then - tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n -fi -bool '/proc filesystem support' CONFIG_PROC_FS y -if [ "$CONFIG_INET" = "y" ]; then - tristate 'NFS filesystem support' CONFIG_NFS_FS y -fi -tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y -tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n -tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS n -tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS n - -mainmenu_option next_comment -comment 'character devices' +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' -bool 'Cyclades async mux support' CONFIG_CYCLADES n -tristate 'Parallel printer support' CONFIG_PRINTER n -bool 'Logitech busmouse support' CONFIG_BUSMOUSE n -bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y -if [ "$CONFIG_PSMOUSE" = "y" ]; then - bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y +bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in fi -bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n -bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n - - -bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n -if [ "$CONFIG_QIC02_TAPE" = "y" ]; then - bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y -if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then - -comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!' -else +source fs/Config.in -comment '>>> Setting runtime QIC-02 configuration is done with qic02conf' -comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/' - -fi -fi - -bool 'QIC-117 tape support' CONFIG_FTAPE n -if [ "$CONFIG_FTAPE" = "y" ]; then - int ' number of ftape buffers' NR_FTAPE_BUFFERS 3 -fi +source drivers/char/Config.in mainmenu_option next_comment comment 'Sound' -bool 'Sound card support' CONFIG_SOUND n +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/sound/Config.in +fi mainmenu_option next_comment comment 'Kernel hacking' -#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n -bool 'Kernel profiling support' CONFIG_PROFILE n +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 -fi -if [ "$CONFIG_SCSI" = "y" ]; then -bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y fi diff -u --recursive --new-file v1.3.35/linux/arch/alpha/defconfig linux/arch/alpha/defconfig --- v1.3.35/linux/arch/alpha/defconfig Thu Jan 1 02:00:00 1970 +++ linux/arch/alpha/defconfig Sun Oct 22 22:17:47 1995 @@ -0,0 +1,170 @@ +# +# Automatically generated make config: don't edit +# + +# +# General setup +# +CONFIG_NATIVE=y +CONFIG_ALPHA_CABRIOLET=y +# CONFIG_ALPHA_SRM is not set +CONFIG_PCI=y +CONFIG_ALPHA_APECS=y +CONFIG_PCI_OPTIMIZE=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y + +# +# Loadable module support +# +# CONFIG_MODVERSIONS is not set + +# +# block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_ST506=y + +# +# Please see drivers/block/README.ide for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_XD is not set + +# +# Networking options +# +# CONFIG_FIREWALL is not set +CONFIG_INET=y +# CONFIG_IP_FORWARD is not set +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set +# CONFIG_NET_IPIP is not set + +# +# (it is safe to leave these untouched) +# +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +CONFIG_INET_SNARL=y +# CONFIG_TCP_NAGLE_OFF is not set +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_NETLINK is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CDrom) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +CONFIG_SCSI_NCR53C7xx=y +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_NCR53C406A is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +# CONFIG_SLIP is not set +# CONFIG_PPP is not set +# CONFIG_SCC is not set +# CONFIG_PLIP is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_ALPHA is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +# CONFIG_APRICOT is not set +CONFIG_DE4X5=y +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_TR is not set +# CONFIG_ARCNET is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_MINIX_FS is not set +# CONFIG_EXT_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_XIA_FS is not set +CONFIG_MSDOS_FS=y +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_NFS_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_SMB_FS is not set + +# +# character devices +# +# CONFIG_CYCLADES is not set +# CONFIG_STALDRV is not set +# CONFIG_PRINTER is not set +# CONFIG_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_QIC02_TAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set diff -u --recursive --new-file v1.3.35/linux/arch/i386/config.in linux/arch/i386/config.in --- v1.3.35/linux/arch/i386/config.in Mon Oct 16 18:38:21 1995 +++ linux/arch/i386/config.in Sun Oct 22 22:16:56 1995 @@ -7,324 +7,78 @@ mainmenu_option next_comment comment 'General setup' -bool 'Kernel math emulation' CONFIG_MATH_EMULATION n -tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD y -bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y -if [ "$CONFIG_ST506" = "y" ]; then - comment 'Please see drivers/block/README.ide for help/info on IDE drives' - bool ' Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD n - if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then - bool ' Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n - else - bool ' Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y - fi - if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then - bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD y - fi -fi - -bool 'XT harddisk support' CONFIG_BLK_DEV_XD n -bool 'Networking support' CONFIG_NET y -bool 'Limit memory to low 16MB' CONFIG_MAX_16M n -bool 'PCI bios support' CONFIG_PCI y +bool 'Kernel math emulation' CONFIG_MATH_EMULATION +bool 'Networking support' CONFIG_NET +bool 'Limit memory to low 16MB' CONFIG_MAX_16M +bool 'PCI bios support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then - bool ' PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE y - if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then - bool ' PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON y - fi + bool ' PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE fi -bool 'System V IPC' CONFIG_SYSVIPC y -tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y +bool 'System V IPC' CONFIG_SYSVIPC +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_BINFMT_ELF" = "y" ]; then -bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF y + bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF fi -#bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586 n +#bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586 #if [ "$CONFIG_M586" = "n" ]; then -bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y +bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 #fi -bool 'SMP Kernel (experimental - gcc2.5.8 only: see Documentation/SMP.txt)' CONFIG_SMP n +bool 'SMP Kernel (experimental - gcc2.5.8 only: see Documentation/SMP.txt)' CONFIG_SMP mainmenu_option next_comment comment 'Loadable module support' -bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n +bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + +source drivers/block/Config.in if [ "$CONFIG_NET" = "y" ]; then -mainmenu_option next_comment -comment 'Networking options' -bool 'TCP/IP networking' CONFIG_INET y -if [ "$CONFIG_INET" = "y" ]; then -bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n -bool 'IP: multicasting' CONFIG_IP_MULTICAST n -bool 'IP: firewalling' CONFIG_IP_FIREWALL n -bool 'IP: accounting' CONFIG_IP_ACCT n -tristate 'IP: tunneling' CONFIG_NET_IPIP n -if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then - bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y - bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n -fi -if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_MULTICAST" = "y" ]; then - bool 'IP: multicast routing(in progress)' CONFIG_IP_MROUTE n -fi -comment '(it is safe to leave these untouched)' -bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n -tristate 'IP: Reverse ARP' CONFIG_INET_RARP n -bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y -bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n -bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y -bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE y -fi -bool 'The IPX protocol' CONFIG_IPX n -bool 'Appletalk DDP' CONFIG_ATALK n -bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n -if [ "$CONFIG_AX25" = "y" ]; then - bool 'Amateur Radio NET/ROM' CONFIG_NETROM n -fi -bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK n -if [ "$CONFIG_NETLINK" = "y" ]; then - bool 'Routing messages' CONFIG_RTNETLINK y -fi + source net/Config.in fi mainmenu_option next_comment comment 'SCSI support' -tristate 'SCSI support' CONFIG_SCSI n - -if [ "$CONFIG_SCSI" = "n" ]; then - -comment 'Skipping SCSI configuration options...' - -else +tristate 'SCSI support' CONFIG_SCSI -comment 'SCSI support type (disk, tape, CDrom)' - -dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD y $CONFIG_SCSI -dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST n $CONFIG_SCSI -dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR y $CONFIG_SCSI -dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG n $CONFIG_SCSI - -comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' - -bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n - -mainmenu_option next_comment -comment 'SCSI low-level drivers' - -dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n $CONFIG_SCSI -dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y $CONFIG_SCSI -dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n $CONFIG_SCSI -dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX n $CONFIG_SCSI -dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n $CONFIG_SCSI -dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA n $CONFIG_SCSI -dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO n $CONFIG_SCSI -dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n $CONFIG_SCSI -dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n $CONFIG_SCSI -bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n -if [ "$CONFIG_PCI" = "y" ]; then - dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n $CONFIG_SCSI +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in fi -dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n $CONFIG_SCSI -bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n -dep_tristate 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n $CONFIG_SCSI -dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n $CONFIG_SCSI -bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n -dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n $CONFIG_SCSI -dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST n $CONFIG_SCSI -dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n $CONFIG_SCSI -dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A n $CONFIG_SCSI -#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n $CONFIG_SCSI -fi - if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' -mainmenu_option next_comment -comment 'Network device support' - -bool 'Network device support' CONFIG_NETDEVICES y -if [ "$CONFIG_NETDEVICES" = "n" ]; then - -comment 'Skipping network driver configuration options...' - -else -tristate 'Dummy net driver support' CONFIG_DUMMY m -tristate 'SLIP (serial line) support' CONFIG_SLIP n -if [ "$CONFIG_SLIP" != "n" ]; then - bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y -fi -tristate 'PPP (point-to-point) support' CONFIG_PPP n -if [ ! "$CONFIG_PPP" = "n" ]; then -comment 'CCP compressors for PPP are only built as modules.' -fi -if [ "$CONFIG_AX25" = "y" ]; then - bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC n -else - bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC n -fi -tristate 'PLIP (parallel port) support' CONFIG_PLIP n -tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n -bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n -bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n -if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then - tristate 'WD80*3 support' CONFIG_WD80x3 n - tristate 'SMC Ultra support' CONFIG_ULTRA n -fi -bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n -bool '3COM cards' CONFIG_NET_VENDOR_3COM y -if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then - tristate '3c501 support' CONFIG_EL1 n - tristate '3c503 support' CONFIG_EL2 n - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - tristate '3c505 support' CONFIG_ELPLUS n - tristate '3c507 support' CONFIG_EL16 n - fi - tristate '3c509/3c579 support' CONFIG_EL3 y -fi -bool 'Other ISA cards' CONFIG_NET_ISA n -if [ "$CONFIG_NET_ISA" = "y" ]; then - tristate 'Cabletron E21xx support' CONFIG_E2100 n - tristate 'DEPCA support' CONFIG_DEPCA n - tristate 'EtherWorks 3 support' CONFIG_EWRK3 n - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - bool 'SEEQ8005 support' CONFIG_SEEQ8005 n - tristate 'AT1700 support' CONFIG_AT1700 n - tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n - tristate 'EtherExpress support' CONFIG_EEXPRESS n - bool 'NI5210 support' CONFIG_NI52 n - bool 'NI6510 support' CONFIG_NI65 n - tristate 'WaveLAN support' CONFIG_WAVELAN n - fi - tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n - tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n - tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 n - tristate 'NE2000/NE1000 support' CONFIG_NE2000 y - if [ "$CONFIG_AX25" = "y" ]; then - bool 'Ottawa PI and PI/2 support' CONFIG_PI y - fi - bool 'SK_G16 support' CONFIG_SK_G16 n -fi -bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA n -if [ "$CONFIG_NET_EISA" = "y" ]; then - if [ "$CONFIG_NET_ALPHA" = "y" ]; then - tristate 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n - fi - tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n - tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5 n -# tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP n -# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n -# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n - bool 'Zenith Z-Note support' CONFIG_ZNET y -fi -bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n -if [ "$CONFIG_NET_POCKET" = "y" ]; then - bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n - tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n - tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n -# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n -# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n -# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n -fi -bool 'Token Ring driver support' CONFIG_TR n -if [ "$CONFIG_TR" = "y" ]; then - tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR y -fi -tristate 'Arcnet support' CONFIG_ARCNET n -fi -fi - -mainmenu_option next_comment -comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' - -bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI n -if [ "$CONFIG_CD_NO_IDESCSI" = "y" ]; then - tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A n - tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD n - tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX n - tristate 'Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD n - if [ "$CONFIG_SBPCD" = "y" ]; then - bool 'Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2 n - if [ "$CONFIG_SBPCD2" = "y" ]; then - bool 'Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3 n - if [ "$CONFIG_SBPCD3" = "y" ]; then - bool 'Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4 n - fi - fi + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in fi - tristate 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n - tristate 'Sony CDU535 CDROM support' CONFIG_CDU535 n - tristate 'Goldstar R420 CDROM support' CONFIG_GSCD n - tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206 n - tristate 'Experimental Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD n - tristate 'Experimental Sanyo H94A CDROM support' CONFIG_SJCD n fi mainmenu_option next_comment -comment 'Filesystems' - -tristate 'Standard (minix) fs support' CONFIG_MINIX_FS y -tristate 'Extended fs support' CONFIG_EXT_FS n -tristate 'Second extended fs support' CONFIG_EXT2_FS y -tristate 'xiafs filesystem support' CONFIG_XIA_FS n -tristate 'msdos fs support' CONFIG_MSDOS_FS y -if [ "$CONFIG_MSDOS_FS" != "n" ]; then - tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n -fi -bool '/proc filesystem support' CONFIG_PROC_FS y -if [ "$CONFIG_INET" = "y" ]; then - tristate 'NFS filesystem support' CONFIG_NFS_FS y -fi -tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y -tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n -tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS n -tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS n - -mainmenu_option next_comment -comment 'character devices' +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' -bool 'Cyclades async mux support' CONFIG_CYCLADES n -bool 'Stallion multiport serial support' CONFIG_STALDRV n -if [ "$CONFIG_STALDRV" = "y" ]; then - tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION n - tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION n -fi -tristate 'Parallel printer support' CONFIG_PRINTER n -tristate 'Logitech busmouse support' CONFIG_BUSMOUSE n -tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n -if [ "$CONFIG_PSMOUSE" = "y" ]; then - bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y +bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in fi -tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n -tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n - -bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n -if [ "$CONFIG_QIC02_TAPE" = "y" ]; then - bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y -if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then +source fs/Config.in -comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!' - -else - -comment '>>> Setting runtime QIC-02 configuration is done with qic02conf' -comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/' - -fi -fi +source drivers/char/Config.in mainmenu_option next_comment comment 'Sound' -tristate 'Sound card support' CONFIG_SOUND n +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/sound/Config.in +fi mainmenu_option next_comment comment 'Kernel hacking' -#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n -bool 'Kernel profiling support' CONFIG_PROFILE n +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 -fi -if [ "$CONFIG_SCSI" = "y" ]; then -bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y fi diff -u --recursive --new-file v1.3.35/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v1.3.35/linux/arch/i386/defconfig Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/defconfig Sun Oct 22 15:57:49 1995 @@ -0,0 +1,131 @@ +# +# Automatically generated make config: don't edit +# + +# +# General setup +# +# CONFIG_MATH_EMULATION is not set +CONFIG_NET=y +# CONFIG_MAX_16M is not set +CONFIG_PCI=y +CONFIG_PCI_OPTIMIZE=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_M486=y +# CONFIG_SMP is not set + +# +# Loadable module support +# +# CONFIG_MODVERSIONS is not set + +# +# block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_ST506=y + +# +# Please see drivers/block/README.ide for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDE=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_XD is not set + +# +# Networking options +# +# CONFIG_FIREWALL is not set +CONFIG_INET=y +# CONFIG_IP_FORWARD is not set +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set +# CONFIG_NET_IPIP is not set + +# +# (it is safe to leave these untouched) +# +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +CONFIG_INET_SNARL=y +# CONFIG_TCP_NAGLE_OFF is not set +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_NETLINK is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +# CONFIG_SLIP is not set +# CONFIG_PPP is not set +# CONFIG_SCC is not set +# CONFIG_PLIP is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_ALPHA is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_LANCE is not set +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +CONFIG_EL3=y +# CONFIG_NET_ISA is not set +# CONFIG_NET_EISA is not set +# CONFIG_NET_POCKET is not set +# CONFIG_TR is not set +# CONFIG_ARCNET is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +CONFIG_MINIX_FS=y +# CONFIG_EXT_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_XIA_FS is not set +CONFIG_MSDOS_FS=y +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_SMB_FS is not set + +# +# character devices +# +# CONFIG_CYCLADES is not set +# CONFIG_STALDRV is not set +# CONFIG_PRINTER is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_PSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_QIC02_TAPE is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set diff -u --recursive --new-file v1.3.35/linux/drivers/Makefile linux/drivers/Makefile --- v1.3.35/linux/drivers/Makefile Mon Oct 16 18:38:21 1995 +++ linux/drivers/Makefile Sat Oct 21 17:50:51 1995 @@ -34,4 +34,9 @@ endif endif +ifdef CONFIG_CD_NO_IDESCSI +SUB_DIRS += cdrom +MOD_SUB_DIRS += cdrom +endif + include $(TOPDIR)/Rules.make diff -u --recursive --new-file v1.3.35/linux/drivers/block/Config.in linux/drivers/block/Config.in --- v1.3.35/linux/drivers/block/Config.in Thu Jan 1 02:00:00 1970 +++ linux/drivers/block/Config.in Sun Oct 22 22:16:56 1995 @@ -0,0 +1,23 @@ +# +# Block device driver configuration +# +mainmenu_option next_comment +comment 'block devices' + +tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD +bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 +if [ "$CONFIG_ST506" = "y" ]; then + comment 'Please see drivers/block/README.ide for help/info on IDE drives' + bool ' Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD + if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then + bool ' Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE + else + bool ' Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE + fi + if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then + bool ' PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON + bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD + fi +fi + +bool 'XT harddisk support' CONFIG_BLK_DEV_XD diff -u --recursive --new-file v1.3.35/linux/drivers/block/Makefile linux/drivers/block/Makefile --- v1.3.35/linux/drivers/block/Makefile Mon Oct 16 18:38:21 1995 +++ linux/drivers/block/Makefile Sat Oct 21 17:52:56 1995 @@ -28,98 +28,6 @@ endif endif -ifeq ($(CONFIG_AZTCD),y) -L_OBJS += aztcd.o -else - ifeq ($(CONFIG_AZTCD),m) - M_OBJS += aztcd.o - endif -endif #CONFIG_AZTCD - -ifeq ($(CONFIG_CDU31A),y) -L_OBJS += cdu31a.o -else - ifeq ($(CONFIG_CDU31A),m) - M_OBJS += cdu31a.o - endif -endif #CONFIG_CDU31A - -ifeq ($(CONFIG_MCD),y) -L_OBJS += mcd.o -else - ifeq ($(CONFIG_MCD),m) - M_OBJS += mcd.o - endif -endif #CONFIG_MCD - -ifeq ($(CONFIG_MCDX),y) -L_OBJS += mcdx.o -else - ifeq ($(CONFIG_MCDX),m) - M_OBJS += mcdx.o - endif -endif #CONFIG_MCDX - -ifeq ($(CONFIG_SBPCD),y) -L_OBJS += sbpcd.o -else - ifeq ($(CONFIG_SBPCD),m) - M_OBJS += sbpcd.o - endif -endif #CONFIG_SBPCD - -ifeq ($(CONFIG_SBPCD2),y) -L_OBJS += sbpcd2.o -endif #CONFIG_SBPCD2 - -ifeq ($(CONFIG_SBPCD3),y) -L_OBJS += sbpcd3.o -endif #CONFIG_SBPCD3 - -ifeq ($(CONFIG_SBPCD4),y) -L_OBJS += sbpcd4.o -endif #CONFIG_SBPCD4 - -ifeq ($(CONFIG_CDU535),y) -L_OBJS += sonycd535.o -else - ifeq ($(CONFIG_CDU535),m) - M_OBJS += sonycd535.o - endif -endif #CONFIG_CDU535 - -ifeq ($(CONFIG_GSCD),y) -L_OBJS += gscd.o -else - ifeq ($(CONFIG_GSCD),m) - M_OBJS += gscd.o - endif -endif #CONFIG_GSCD - -ifeq ($(CONFIG_CM206),y) -L_OBJS += cm206.o -else - ifeq ($(CONFIG_CM206),m) - M_OBJS += cm206.o - endif -endif #CONFIG_CM206 - -ifeq ($(CONFIG_OPTCD),y) -L_OBJS += optcd.o -else - ifeq ($(CONFIG_OPTCD),m) - M_OBJS += optcd.o - endif -endif #CONFIG_OPTCD - -ifeq ($(CONFIG_SJCD),y) -L_OBJS += sjcd.o -else - ifeq ($(CONFIG_SJCD),m) - M_OBJS += sjcd.o - endif -endif #CONFIG_SJCD - ifeq ($(CONFIG_BLK_DEV_HD),y) L_OBJS += hd.o endif diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.aztcd linux/drivers/block/README.aztcd --- v1.3.35/linux/drivers/block/README.aztcd Mon Oct 16 18:38:21 1995 +++ linux/drivers/block/README.aztcd Thu Jan 1 02:00:00 1970 @@ -1,793 +0,0 @@ -$Id: README.aztcd,v 1.80 1995/10/11 19:37:49 root Exp root $ - Readme-File README.aztcd - for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 - CD-ROM Driver - Version 1.7 and newer - (for other drives see 6.-8.) - -NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE - A PROPRIETARY INTERFACE (implemented on a sound card or on a - ISA-AT-bus card). - IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE, - such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE - WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE - USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER ! - THE STANDARD-KERNEL 1.2.x NOW ALSO SUPPORTS IDE-CDROM-DRIVES, SEE THE - HARDDISK (!) SECTION OF make config, WHEN COMPILING A NEW KERNEL!!! ----------------------------------------------------------------------------- - -Contents of this file: - 1. NOTE - 2. INSTALLATION - 3. CONFIGURING YOUR KERNEL - 4. RECOMPILING YOUR KERNEL - 4.1 AZTCD AS A RUN-TIME LOADABLE MODULE - 4.2 CDROM CONNECTED TO A SOUNDCARD - 5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS - 5.1 MULTISESSION SUPPORT - 5.2 STATUS RECOGNITION - 5.3 DOSEMU's CDROM SUPPORT - 6. BUG REPORTS - 7. OTHER DRIVES - 8. IF YOU DON'T SUCCEED ... DEBUGGING - 9. TECHNICAL HISTORY OF THE DRIVER - 10. ACKNOWLEDGMENTS - 11. PROGRAMMING ADD ONS: CDPLAY.C - APPENDIX: Source code of cdplay.c ----------------------------------------------------------------------------- - -1. NOTE -This software has been successfully in alpha and beta test for quite a long -time with AZTECH CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110 -and has proven to be stable with kernel versions 1.0.9 to 1.2.0. But with -any software there still may be bugs in it. So if you encounter problems, -you are invited to help us improve this software. Please send me a detailed -bug report (see chapter BUG REPORTS). You are also invited in helping us to -increase the number of drives, which are supported. - -Please read the README-files carefully and always keep a backup copy of your -old kernel, in order to reboot if something goes wrong! - - -2. INSTALLATION -If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz -(xx=version number) and not included in the standard Linux kernel, read the -file AZTECH.CDROM.README included in that package to install it. The -standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'. -The driver consists of a header file 'aztcd.h', which normally should reside -in /usr/include/linux and the source code 'aztcd.c', which normally resides in -/usr/src/linux/drivers/block. It uses /dev/aztcd0, which must be a valid block -device with major number 29 and reside in directory /dev. To mount a CD-ROM, -your kernel needs to have the ISO9660-filesystem support included. - - -3. CONFIGURING YOUR KERNEL -If your kernel is already configured for using the AZTECH driver you will -see the following message while Linux boots: - Aztech CD-ROM Init: DriverVersion= BaseAddress= - Aztech CD-ROM Init: FirmwareVersion=>> - Aztech CD-ROM Init: 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=, 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=,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= BaseAddress= - Aztech CD-ROM Init: FirmwareVersion= - Aztech CD-ROM Init: 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= -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=,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 -#include -#include -#include -#include -#include -#include -#include - -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_trk0last) 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_tracklast) entry.cdte_track=last; - entry.cdte_format=CDROM_MSF; - if (ioctl(handle,cmd,&entry)) - { printf("Drive error or invalid track no.\n"); - } - else - { printf("Mode %d Track, starts at %d:%d:%d\n", \ - entry.cdte_adr,entry.cdte_addr.msf.minute, \ - entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame); - } - } - break; - case 'a': cmd=CDROMPLAYMSF; - printf("Address (min:sec:frame) "); - scanf("%d:%d:%d",&arg1,&arg2,&arg3); - msf.cdmsf_min0 =arg1; - msf.cdmsf_sec0 =arg2; - msf.cdmsf_frame0=arg3; - if (msf.cdmsf_sec0 > 59) msf.cdmsf_sec0 =59; - if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74; - msf.cdmsf_min1=60; - msf.cdmsf_sec1=00; - msf.cdmsf_frame1=00; - if (ioctl(handle,cmd,&msf)) - { printf("Drive error or invalid address\n"); - } - break; -#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/ - case 'd': cmd=CDROMREADCOOKED; - printf("Address (min:sec:frame) "); - scanf("%d:%d:%d",&arg1,&arg2,&arg3); - azt.msf.cdmsf_min0 =arg1; - azt.msf.cdmsf_sec0 =arg2; - azt.msf.cdmsf_frame0=arg3; - if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59; - if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74; - if (ioctl(handle,cmd,&azt.msf)) - { printf("Drive error, invalid address or unsupported command\n"); - } - k=0; - getchar(); - for (i=0;i<128;i++) - { printf("%4d:",i*16); - for (j=0;j<16;j++) - { printf("%2x ",azt.buf[i*16+j]); - } - for (j=0;j<16;j++) - { if (isalnum(azt.buf[i*16+j])) - printf("%c",azt.buf[i*16+j]); - else - printf("."); - } - printf("\n"); - k++; - if (k>=20) - { printf("press ENTER to continue\n"); - getchar(); - k=0; - } - } - break; - case 'w': cmd=CDROMREADRAW; - printf("Address (min:sec:frame) "); - scanf("%d:%d:%d",&arg1,&arg2,&arg3); - azt.msf.cdmsf_min0 =arg1; - azt.msf.cdmsf_sec0 =arg2; - azt.msf.cdmsf_frame0=arg3; - if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59; - if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74; - if (ioctl(handle,cmd,&azt)) - { printf("Drive error, invalid address or unsupported command\n"); - } - k=0; - for (i=0;i<147;i++) - { printf("%4d:",i*16); - for (j=0;j<16;j++) - { printf("%2x ",azt.buf[i*16+j]); - } - for (j=0;j<16;j++) - { if (isalnum(azt.buf[i*16+j])) - printf("%c",azt.buf[i*16+j]); - else - printf("."); - } - printf("\n"); - k++; - if (k>=20) - { getchar(); - k=0; - } - } - break; -#endif - case 'v': cmd=CDROMVOLCTRL; - printf("--Channel 0 Left (0-255): "); - scanf("%d",&arg1); - printf("--Channel 1 Right (0-255): "); - scanf("%d",&arg2); - volctrl.channel0=arg1; - volctrl.channel1=arg2; - volctrl.channel2=0; - volctrl.channel3=0; - if (ioctl(handle,cmd,&volctrl)) - { printf("Drive error or unsupported command\n"); - } - break; - case 'q': if (close(handle)) printf("Drive Error: CLOSE\n"); - exit(0); - case 'h': help(); - break; - default: printf("unknown command\n"); - break; - } - } - } - return 0; -} diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.cdu31a linux/drivers/block/README.cdu31a --- v1.3.35/linux/drivers/block/README.cdu31a Fri Jul 7 08:54:44 1995 +++ linux/drivers/block/README.cdu31a Thu Jan 1 02:00:00 1970 @@ -1,160 +0,0 @@ - Tips for using cdu31a.c, the driver for Sony CDU-31A and CDU-33A CDROM - drives. - - Corey Minyard (minyard@wf-rch.cirr.com) - - Colossians 3:17 - - The Sony interface device driver handles Sony interface CDROM - drives and provides a complete block-level interface as well as an - ioctl() interface compatible with the Sun (as specified in - include/linux/cdrom.h). With this interface, CDROMs can be - accessed and standard audio CDs can be played back normally. - - WARNING - All autoprobes have been removed from the driver. - You MUST configure the CDU31A via a LILO config - at boot time or in lilo.conf. I have the - following in my lilo.conf: - - append="cdu31a=0x1f88,0,PAS" - - The first number is the I/O base address of the - card. The second is the interrupt (0 means none). - The third should be "PAS" if on a Pro-Audio - spectrum, or nothing if on something else. - - This interface is (unfortunately) a polled interface. This is - because most Sony interfaces are set up with DMA and interrupts - disables. Some (like mine) do not even have the capability to - handle interrupts or DMA. For this reason you will see a lot of - the following: - - retry_count = jiffies+ SONY_JIFFIES_TIMEOUT; - while ((retry_count > jiffies) && (! ' -The driver should report his results now. - -That's it! Mount a disk, i.e. 'mount -rt iso9660 /dev/gscd0 /cdrom' - -Feel free to report errors and suggestions to the following address. -Be sure, I'm very happy to receive your comments! - - Oliver Raupach Hannover, Juni 1995 -(raupach@nwfs1.rz.fh-hannover.de) diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.ide linux/drivers/block/README.ide --- v1.3.35/linux/drivers/block/README.ide Mon Sep 18 14:53:48 1995 +++ linux/drivers/block/README.ide Fri Oct 20 10:10:39 1995 @@ -36,6 +36,8 @@ - this support also enables the secondary i/f on most cards NEW! - support for secondary interface on the FGI/Holtek HT-6560B VLB i/f - use kernel command line option: ide1=ht6560 +NEW! - experimental "fast" speed support for QD6580 interfaces + - use kernel command line option: ide0=qd6580 NEW! - experimental support for DTC-2278D interfaces - use kernel command line option: ide1=dtc2278 NEW! - support for drives with a stuck WRERR_STAT bit @@ -53,6 +55,10 @@ For work in progress, see the comments in ide.c, ide-cd.c, and triton.c. +Note that there is now a group actively working on support for the Promise +caching IDE cards, such as the DC4030VL, and early results are encouraging. +Look for this support to be added to the kernel soon. + *** IMPORTANT NOTICES (for kernel versions prior to 1.3.22) *** ================= @@ -212,28 +218,33 @@ "hdx=" is recognized for all "x" from "a" to "h", such as "hdc". "idex=" is recognized for all "x" from "0" to "3", such as "ide1". - - "hdx=noprobe" : drive may be present, but do not probe for it - "hdx=nowerr" : ignore the WRERR_STAT bit on this drive - "hdx=cdrom" : drive is present, and is a cdrom drive - "hdx=cyl,head,sect" : disk drive is present, with specified geometry - - "idex=noprobe" : do not attempt to access/use this interface - "idex=base" : probe for an interface at the addr specified, - where "base" is usually 0x1f0 or 0x170 - and "ctl" is assumed to be "base"+0x206 - and "irq" will be probed for - "idex=base,ctl" : specify both base and ctl - "idex=base,ctl,irq" : specify base, ctl, and irq number - -The following two are valid ONLY on ide0 or ide1: - - "idex=dtc2278" : look for and try to initialize a dtc2278 - "idex=serialize" : do not overlap operations on ide0 and ide1. - "idex=ht6560b" : enables use of HT6560B secondary i/f - "idex=cmd640_vlb" : required for VLB cards with the CMD640 chip - (not for PCI versions) - + + "hdx=noprobe" : drive may be present, but do not probe for it + "hdx=nowerr" : ignore the WRERR_STAT bit on this drive + "hdx=cdrom" : drive is present, and is a cdrom drive + "hdx=cyl,head,sect" : disk drive is present, with specified geometry + + "idex=noprobe" : do not attempt to access/use this interface + "idex=base" : probe for an interface at the addr specified, + where "base" is usually 0x1f0 or 0x170 + and "ctl" is assumed to be "base"+0x206 + "idex=base,ctl" : specify both base and ctl + "idex=base,ctl,irq" : specify base, ctl, and irq number + + The following two are valid ONLY on ide0 or ide1, + and the defaults for the base,ctl ports must not be altered. + + "idex=serialize" : do not overlap operations on ide0 and ide1. + "idex=dtc2278" : enables use of DTC2278 secondary i/f + "idex=ht6560b" : enables use of HT6560B secondary i/f + "idex=cmd640_vlb" : required for VLB cards with the CMD640 chip + (not for PCI -- automatically detected) + + This option is valid ONLY on ide0, and the defaults for the base,ctl ports + must not be altered. + + "ide0=qd6580" : select "fast" interface speed on a qd6580 interface + Everything else is rejected with a "BAD OPTION" message. ================================================================================ diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.mcd linux/drivers/block/README.mcd --- v1.3.35/linux/drivers/block/README.mcd Sun Sep 3 12:26:50 1995 +++ linux/drivers/block/README.mcd Thu Jan 1 02:00:00 1970 @@ -1,4 +0,0 @@ -This driver does not support XA or MultiSession CDs (PhotoCDs). Use the -experimental driver mcdx.c for that. - -You can use mcd for one interface, and mcdx for another. diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.mcdx linux/drivers/block/README.mcdx --- v1.3.35/linux/drivers/block/README.mcdx Sun Sep 3 12:26:50 1995 +++ linux/drivers/block/README.mcdx Thu Jan 1 02:00:00 1970 @@ -1,44 +0,0 @@ -This is a first attempt to create an `improved' driver for the Mitsumi drives. -It is able to "live together" with mcd.c, if you have at least two Mitsumi -drives: each driver can use his own drive. - -To allow this "coexistence" as long as mcdx.c is not a superset of mcd.c, -this driver has to use its own device files. We use MAJOR 20 for it. So, -you have to do - - # mknod /dev/mcdx0 b 20 0 - # mknod /dev/mcdx1 b 20 1 - -and so on, one entry for each drive to support, once. - -If you are using the driver as a module, you can specify your ports and IRQs -like - - # insmod mcdx.o mcdx=0x300,11,0x304,5 - -and so on ("address,IRQ" pairs). -This will override the configuration in mcdx.h. - -This driver: - - o handles XA (and hopefully) multi session CDs as well as - ordinary CDs; - o supports up to 5 drives (of course, you'll need free - IRQs, i/o ports and slots); - o uses much less kernel memory than the standard mcd driver - (no extra driver internal buffers!). - o plays audio (like the `old' driver, I hope) - -This version doesn't support yet: - - o shared IRQs (but it seems to be possible - I've successfully - connected two drives to the same irq. So it's `only' a - problem of the driver.) - -This driver never will: - - o Read digital audio (i.e. copy directly), due to missing - hardware features. - - -heiko@lotte.sax.de diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.optcd linux/drivers/block/README.optcd --- v1.3.35/linux/drivers/block/README.optcd Mon Aug 28 14:52:18 1995 +++ linux/drivers/block/README.optcd Thu Jan 1 02:00:00 1970 @@ -1,46 +0,0 @@ -This is the README file for the Optics Storage 8000 AT CDROM device driver. - -The driver contains code to enable an ISP16 interface if it finds one. It -didn't originally (although this README erroneously said so), because I think -this kind of code should go into its own module. But having to use a hack all -the time in order to use a part of the standard kernel started to annoy me, so -I copied the ISP16 code by Eric van der Maarel (maarel@marin.nl) from Vadim -Model's Sanyo sjcd driver. I'll remove it again from this driver when we have -some common way to talk to ISP16 interfaces. - -My original configuration code for the ISP-16 card can get found at - dutette.et.tudelft.nl:/pub/linux/ -and at Eberhard's mirror - ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/ - -Much more elaborate information can be found at ftp:rbrf.msk.su, -where Vadim Model (vadim@rbrf.msk.su) has made available an ISP16 -device driver. -Vadim's directory is - rbrf.msk.su:/linux/mediamagic/ -and Eberhard is holding a mirror at - ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/ - - -Before you can use the driver, you have to create the device file once: - # mknod /dev/optcd0 b 17 0 - -To specify the base address if the driver is "compiled-in" to your kernel, -you can use the kernel command line item (LILO option) - optcd=0x340 -with the right address. - -If you have compiled optcd as a module, you can load it with - # insmod /usr/src/linux/modules/optcd.o -or - # insmod /usr/src/linux/modules/optcd.o optcd=0x340 -with the matching address value of your interface card. - -I have tried the module with several 1.2.x kernel versions, and it seems to -work, as far as I tested. It also seems to work for several 1.3.x versions. -If you use it, I'd appreciate success/failure reports. If you find a bug, -try recompiling the driver with some strategically chosen #undef DEBUG_...'s -changed into #defines (you'll find them in .../include/linux/optcd.h) and -include the messages generated in your bug report. Good luck. - -Leo Spiekman (spiekman@dutette.et.tudelft.nl) diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd --- v1.3.35/linux/drivers/block/README.sbpcd Fri Sep 15 11:12:59 1995 +++ linux/drivers/block/README.sbpcd Thu Jan 1 02:00:00 1970 @@ -1,1049 +0,0 @@ -This README belongs to release 3.9 or newer of the SoundBlaster Pro -(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC) -CD-ROM driver for Linux. - -Sbpcd really, really is NOT for ANY IDE/ATAPI drive! -Not even if you have an "original" SoundBlaster card with an IDE interface! -So, you better have a look into README.ide if your port address is 0x1F0, -0x170, 0x1E8, 0x168 or similar. -I get tons of mails from IDE/ATAPI drive users - I really can't continue -any more to answer them all. So, if your drive/interface information sheets -mention "IDE" (primary, secondary, tertiary, quaternary) and the DOS driver -invoking line within your CONFIG.SYS is using an address below 0x230: -DON'T ROB MY LAST NERVE - jumper your interface to address 0x170 and IRQ 15 -(that is the "secondary IDE" configuration), set your drive to "master" and -use ide-cd as your driver. If you do not have a second IDE hard disk, use the -LILO commands - hdb=noprobe hdc=cdrom -and get lucky. -To make it fully clear to you: if you mail me about IDE/ATAPI drive problems, -my answer is above, and I simply will discard your mail, hoping to stop the -flood and to find time to lead my 12-years old son towards happy computing. - -The driver is able to drive the whole family of "traditional" AT-style (that -is NOT the new "Enhanced IDE" or "ATAPI" drive standard) Matsushita, -Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The -well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563. -CR-574 is an IDE/ATAPI drive. - -The Longshine LCS-7260 is a double-speed drive which uses the "old" -Matsushita command set. It is supported - with help by Serge Robyns. - -There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563 -with a special controller board. This drive is supported (the interface is -of the "LaserMate" type), and it is possibly the best buy today (cheaper than -an internal drive, and you can use it as an internal, too - f.e. plug it into -a soundcard). - -CreativeLabs has a new drive "CD200" and a similar drive "CD200F". The latter -is made by Funai and sometimes named "E2550UA". Support is under construction - - CD200F should work, CD200 is still giving problems. -Drive detection and playing audio should work. I need qualified feedback -about the bugs within the data functions or a drive (I never saw a CD200). - -The quad-speed TEAC CD-55A drive is supported, but still does not reach "full -speed". The data rate already reaches 500 kB/sec if you set SBP_BUFFER_FRAMES -to 64 (it is not recommended to do that for normal "file access" usage, but it -can speed up things a lot if you use something like "dd" to read from the -drive; I use it for verifying self-written CDs this way). -The drive itself is able to deliver 600 kB/sec, so this has to get a point of -work; with the normal setup, the performance currently is not even as good as -double-speed. - -This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives, -and again: this driver is in no way usable for any IDE/ATAPI drive. If you -think your drive should work and it doesn't: send me the DOS driver for your -beast (gzipped + uuencoded) and your CONFIG.SYS if you want to ask me for help, -and include an original log message excerpt, and try to give all information -a complete idiot needs to understand your hassle already with your first -mail. And if you want to say "as I have mailed you before", be sure that I -don't remember your "case" by such remarks; at the moment, I have some -hundreds open correspondences about Linux CDROM questions (hope to reduce if -the IDE/ATAPI user questions disappear). - - -This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy, -SoundFX, Mozart, ...) and with the "no-sound" cards (Panasonic CI-101P, -LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...). - -It finally works now with the "configurable" interface "Sequoia S-1000", too, -which is found on the Spea Media FX and Ensonic Soundscape sound cards. You -have to specify the type "SBPRO 2" and the true CDROM port address with it, -not the "configuration port" address. - -If you have a sound card which needs a "configuration driver" instead of -jumpers for interface types and addresses (like Mozart cards) - those -drivers get invoked before the DOS CDROM driver in your CONFIG.SYS, typical -names are "cdsetup.sys" and "mztinit.sys" -, let the sound driver do the -CDROM port configuration (the leading comments within -linux/drivers/sound/mad16.c are just for you!). Hannu Savolainen's mad16.c -code is able to set up my Mozart card - I simply had to add - #define MAD16_CONF 0x06 - #define MAD16_CDSEL 0x03 -to configure the CDROM interface for type "Panasonic" (LaserMate) and address -0x340. - -The interface type has to get configured in /usr/include/linux/sbpcd.h, -because the register layout is different between the "SoundBlaster" and the -"LaserMate" type. - -I got a report that the TEAC interface card "I/F E117098" is of type -"SoundBlaster" (i.e. you have to set SBPRO to 1) even with the addresses -0x300 and above. This is unusual, and it can't get covered by the auto -probing scheme. -If auto-probing found the drive, the address is correct. The reported type -may be wrong. A "mount" will give success only if the interface type is set -right. Playing audio should work with a wrong set interface type, too. - -With some TEAC and some CD200 drives I have seen interface cards which seem -to lack the "drive select" lines; always drive 0 gets addressed. To avoid -"mirror drives" (four drives detected where you only have one) with such -interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0 (if -possible). - - -Up to 4 drives per interface card, and up to 4 interface cards are supported. -All supported drive families can be mixed, but the CR-521 drives are -hard-wired to drive ID 0. The drives have to use different drive IDs, and each -drive has to get a unique minor number (0...3), corresponding indirectly to -its drive ID. -The drive IDs may be selected freely from 0 to 3 - they do not have to be in -consecutive order. - -As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible -to change old drives to any ID, too. He writes in this sense: - "In order to be able to use more than one single speed drive - (they do not have the ID jumpers) you must add a DIP switch - and two resistors. The pads are already on the board next to - the power connector. You will see the silkscreen for the - switch if you remove the top cover. - 1 2 3 4 - ID 0 = x F F x O = "on" - ID 1 = x O F x F = "off" - ID 2 = x F O x x = "don't care" - ID 3 = x O O x - Next to the switch are the positions for R76 (7k) and R78 - (12k). I had to play around with the resistor values - ID 3 - did not work with other values. If the values are not good, - ID 3 behaves like ID 0." - -To use more than 4 drives, you simply need a second controller card at a -different address and a second cable. - -The driver supports reading of data from the CD and playing of audio tracks. -The audio part should run with WorkMan, xcdplayer, with the "non-X11" products -CDplayer and WorkBone - tell me if it is not compatible with other software. - -With the CR-562 and CR-563 drives, the reading of audio frames is possible. -This is implemented by an IOCTL function which reads READ_AUDIO frames of -2352 bytes at once (configurable with the "READ_AUDIO" define, default is 0). -Reading the same frame a second time gives different data; the frame data -start at a different position, but all read bytes are valid, and we always -read 98 consecutive chunks (of 24 Bytes) as a frame. Reading more than 1 frame -at once possibly misses some chunks at each frame boundary. This lack has to -get corrected by external, "higher level" software which reads the same frame -again and tries to find and eliminate overlapping chunks (24-byte-pieces). - -The transfer rate with reading audio (1-frame-pieces) currently is very slow. -This can be better reading bigger chunks, but the "missing" chunks possibly -occur at the beginning of each single frame. -The software interface possibly may change a bit the day the SCSI driver -supports it too. - -With all but the CR-52x drives, MultiSession is supported. -Photo CDs work (the "old" drives like CR-521 can access only the first -session of a photoCD). -At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to -convert photo CD image files and Gerd Knorr's viewing utility. - -The transfer rate will reach 150 kB/sec with CR-52x drives, 300 kB/sec with -CR-56x drives, and currently not more than 500 kB/sec (usually less than -250 kB/sec) with the TEAC quad speed drives. -XA (PhotoCD) disks with "old" drives give only 50 kB/sec. - -This release consists of -- this README file -- the driver file linux/drivers/block/sbpcd.c -- the stub files linux/drivers/block/sbpcd[234].c -- the header file linux/include/linux/sbpcd.h. - - -To install: ------------ - -1. Setup your hardware parameters. Though the driver does "auto-probing" at a - lot of (not all possible!) addresses, this step is recommended for - every-day use. You should let sbpcd auto-probe once and use the reported - address if a drive got found. The reported type may be incorrect; it is - correct if you can mount a data CD. There is no choice for you with the - type; only one is the right, the others are deadly wrong. - - a. Go into /usr/src/linux/include/linux/sbpcd.h and configure it for your - hardware (near the beginning): - a1. Set it up for the appropriate type of interface board. - "Original" CreativeLabs sound cards need "SBPRO 1". - Most "compatible" sound cards (almost all "non-CreativeLabs" cards) - need "SBPRO 0". - The "no-sound" board from OmniCd needs the "SBPRO 1" setup. - All other "no-sound" boards need the "SBPRO 0" setup. - Possibly some TEAC "no-sound" boards need the "SBPRO 1" setup. - The Spea Media FX sound card needs "SBPRO 2". - sbpcd.c holds some examples in its auto-probe list. - If you configure "SBPRO" wrong, the playing of audio CDs will work, - but you will not be able to mount a data CD. - a2. Tell the address of your CDROM_PORT (not of the sound port). - a3. If 4 drives get found, but you have only one, set MAX_DRIVES to 1. - a4. Set DISTRIBUTION to 0. - b. Additionally for 2.a1 and 2.a2, the setup may be done during - boot time (via the "kernel command line" or "LILO option"): - sbpcd=0x230,SoundBlaster - or - sbpcd=0x320,LaserMate - or - sbpcd=0x330,SPEA - This is especially useful if you install a fresh distribution. - If the second parameter is a number, it gets taken as the type - setting; 0 is "LaserMate", 1 is "SoundBlaster". - So, for example - sbpcd=0x230,1 - is equivalent to - sbpcd=0x230,SoundBlaster - -2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita - CD-ROM support and for ISO9660 FileSystem support. If you do not have a - second, third, or fourth controller installed, do not say "y" to the - secondary Matsushita CD-ROM questions. - -3. Then do a "make dep", then make the kernel image ("make zlilo" or else). - -4. Make the device file(s). This step usually already has been done by the - MAKEDEV script. - The driver uses MAJOR 25, so, if necessary, do - mknod /dev/sbpcd b 25 0 (if you have only one drive) - and/or - mknod /dev/sbpcd0 b 25 0 - mknod /dev/sbpcd1 b 25 1 - mknod /dev/sbpcd2 b 25 2 - mknod /dev/sbpcd3 b 25 3 - to make the node(s). - - The "first found" drive gets MINOR 0 (regardless to its jumpered ID), the - "next found" (at the same cable) gets MINOR 1, ... - - For a second interface board, you have to make nodes like - mknod /dev/sbpcd4 b 26 0 - mknod /dev/sbpcd5 b 26 1 - and so on. Use the MAJORs 26, 27, 28. - - If you further make a link like - ln -s sbpcd /dev/cdrom - you can use the name /dev/cdrom, too. - -5. Reboot with the new kernel. - -You should now be able to do - mkdir /CD -and - mount -rt iso9660 /dev/sbpcd /CD -or - mount -rt iso9660 -o block=2048 /dev/sbpcd /CD -and see the contents of your CD in the /CD directory. -To use audio CDs, a mounting is not recommended (and it would fail if the -first track is not a data track). - - -Using sbpcd as a "loadable module": ------------------------------------ - -If you do NOT select "Matsushita/Panasonic CDROM driver support" during the -"make config" of your kernel, you can build the "loadable module" sbpcd.o. -Read /usr/src/linux/README.modules on this. - -If sbpcd gets used as a module, the support of more than one interface -card (i.e. drives 4...15) is disabled. - -You can specify interface address and type with the "insmod" command like: - # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x340,0 -or - # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1 -where the last number represents the SBPRO setting (no strings allowed here). - - -Things of interest: -------------------- - -The driver is configured to try the LaserMate type of interface at I/O port -0x0340 first. If this is not appropriate, sbpcd.h should get changed -(you will find the right place - just at the beginning). - -No DMA and no IRQ is used. - -To reduce or increase the amount of kernel messages, edit sbpcd.c and play -with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug"). -Don't forget to reflect what you do; enabling all DBG_xxx switches at once -may crash your system. - -The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to -specify "block=2048" as a mount option. Doing this will disable the direct -execution of a binary from the CD; you have to copy it to a device with the -standard BLOCK_SIZE (1024) before. So, do not use this if your system is -directly "running from the CDROM" (like some of YGGDRASIL's installation -variants). There are CDs on the market (like the german "unifix" Linux -distribution) which MUST get handled with a block_size of 1024. Generally, -one can say all the CDs which hold files of the name YMTRANS.TBL are defective; -do not use block=2048 with those. - -Within sbpcd.h, you will find some "#define"s (f.e. EJECT and JUKEBOX). With -that, you can configure the driver for some special things. -You can use the appended program "cdtester" to set the auto-eject feature -during runtime. Jeff Tranter's "eject" utility can do this, too (and more) -for you. - -There is an ioctl CDROMMULTISESSION to obtain with a user program if -the CD is an XA disk and - if it is - where the last session starts. The -"cdtester" program illustrates how to call it. - - -Auto-probing at boot time: --------------------------- - -The driver does auto-probing at many well-known interface card addresses, -but not all: -Some probings can cause a hang if an NE2000 ethernet card gets touched, because -SBPCD's auto-probing happens before the initialization of the net drivers. -Those "hazardous" addresses are excluded from auto-probing; the "kernel -command line" feature has to be used during installation if you have your -drive at those addresses. The "module" version is allowed to probe at those -addresses, too. - -The auto-probing looks first at the configured address resp. the address -submitted by the kernel command line. With this, it is possible to use this -driver within installation boot floppies, and for any non-standard address, -too. - -Auto-probing will make an assumption about the interface type ("SBPRO" or not), -based upon the address. That assumption may be wrong (initialization will be -o.k., but you will get I/O errors during mount). In that case, use the "kernel -command line" feature and specify address & type at boot time to find out the -right setup. - -For every-day use, address and type should get configured within sbpcd.h. That -will stop the auto-probing due to success with the first try. - -The kernel command "sbpcd=0" suppresses each auto-probing and causes -the driver not to find any drive; it is meant for people who love sbpcd -so much that they do not want to miss it, even if they miss the drives. ;-) - -If you configure "#define CDROM_PORT 0" in sbpcd.h, the auto-probing is -initially disabled and needs an explicit kernel command to get activated. -Once activated, it does not stop before success or end-of-list. This may be -useful within "universal" CDROM installation boot floppies (but using the -loadable module would be better because it allows an "extended" auto-probing -without fearing NE2000 cards). - -To shorten the auto-probing list to a single entry, set DISTRIBUTION 0 within -sbpcd.h. - - -Setting up address and interface type: --------------------------------------- - -If your I/O port address is not 0x340, you have to look for the #defines near -the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and -change CDROM_PORT to the address of your CDROM I/O port. - -Almost all of the "SoundBlaster compatible" cards behave like the no-sound -interfaces, i.e. need SBPRO 0! - -With "original" SB Pro cards, an initial setting of CD_volume through the -sound cards MIXER register gets done. -If you are using a "compatible" sound card of types "LaserMate" or "SPEA", -you can set SOUND_BASE (in sbpcd.h) to get it done with your card, too... - - -Using audio CDs: ----------------- - -Workman, WorkBone, xcdplayer, cdplayer and the nice little tool "cdplay" (see -README.aztcd from the Aztech driver package) should work. - -The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer wants -"/dev/rsr0", workman loves "/dev/sr0" or "/dev/cdrom" - so, do the appropriate -links for using them without the need of supplying parameters. - - -Copying audio tracks: ---------------------- - -The following program will copy track 1 (or a piece of it) from an audio CD -into the file "track01": - -/*=================== begin program ========================================*/ -/* - * read an audio track from a CD - * - * (c) 1994 Eberhard Moenkeberg - * 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 -#include -#include - -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 - * published under the GPL - * - * made under heavy use of the "Tiny Audio CD Player" - * from Werner Zimmermann - * (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 -#include -#include -#include -#include - -#ifdef AZT_PRIVATE_IOCTLS -#include -#endif AZT_PRIVATE_IOCTLS -#ifdef SBP_PRIVATE_IOCTLS -#include -#include -#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 \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_trk0last) 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_trk0last) 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 ((ihdr.cdth_trk1)) - printf("invalid track number.\n"); - else - printf("TocEntry %02d: adr=%01X ctrl=%01X msf=%02d:%02d:%02d mode=%02X\n", - TocEntry[i].cdte_track, - TocEntry[i].cdte_adr, - TocEntry[i].cdte_ctrl, - TocEntry[i].cdte_addr.msf.minute, - TocEntry[i].cdte_addr.msf.second, - TocEntry[i].cdte_addr.msf.frame, - TocEntry[i].cdte_datamode); - break; - case 'A': /* read audio data into file */ - printf("Address (min:sec:frm) ? "); - scanf("%d:%d:%d",&i1,&i2,&i3); - read_audio.addr.msf.minute=i1; - read_audio.addr.msf.second=i2; - read_audio.addr.msf.frame=i3; - read_audio.addr_format=CDROM_MSF; - printf("# of frames ? "); - scanf("%d",&i1); - read_audio.nframes=i1; - k=read_audio.nframes*CD_FRAMESIZE_RAW; - read_audio.buf=malloc(k); - if (read_audio.buf==NULL) - { - printf("can't malloc %d bytes.\n",k); - break; - } - sprintf(filename,"audio_%02d%02d%02d_%02d.%02d\0", - read_audio.addr.msf.minute, - read_audio.addr.msf.second, - read_audio.addr.msf.frame, - read_audio.nframes, - ++sequence); - datafile=creat(filename, 0755); - if (datafile<0) - { - printf("can't open datafile %s.\n",filename); - break; - } - rc=ioctl(drive,CDROMREADAUDIO,&read_audio); - if (rc!=0) - { - printf("CDROMREADAUDIO: rc=%d.\n",rc); - } - else - { - rc=write(datafile,&read_audio.buf,k); - if (rc!=k) printf("datafile I/O error (%d).\n",rc); - } - close(datafile); - break; - case 'X': /* set EJECT_SW (0: disable, 1: enable auto-ejecting) */ - scanf("%d",&i); - rc=ioctl(drive,CDROMEJECT_SW,i); - if (rc!=0) - printf("CDROMEJECT_SW: rc=%d.\n",rc); - else - printf("EJECT_SW set to %d\n",i); - break; - case 'M': /* get the multisession redirection info */ - ms_info.addr_format=CDROM_LBA; - rc=ioctl(drive,CDROMMULTISESSION,&ms_info); - if (rc!=0) - { - printf("CDROMMULTISESSION(lba): rc=%d.\n",rc); - } - else - { - if (ms_info.xa_flag) printf("MultiSession offset (lba): %d (0x%06X)\n",ms_info.addr.lba,ms_info.addr.lba); - else - { - printf("this CD is not an XA disk.\n"); - break; - } - } - ms_info.addr_format=CDROM_MSF; - rc=ioctl(drive,CDROMMULTISESSION,&ms_info); - if (rc!=0) - { - printf("CDROMMULTISESSION(msf): rc=%d.\n",rc); - } - else - { - if (ms_info.xa_flag) - printf("MultiSession offset (msf): %02d:%02d:%02d (0x%02X%02X%02X)\n", - ms_info.addr.msf.minute, - ms_info.addr.msf.second, - ms_info.addr.msf.frame, - ms_info.addr.msf.minute, - ms_info.addr.msf.second, - ms_info.addr.msf.frame); - else printf("this CD is not an XA disk.\n"); - } - break; -#ifdef SBP_PRIVATE_IOCTLS - case 'Y': /* set the driver's message level */ -#if 0 /* not implemented yet */ - printf("enter switch name (f.e. DBG_CMD): "); - scanf("%s",&dbg_switch); - j=get_dbg_num(dbg_switch); -#else - printf("enter DDIOCSDBG switch number: "); - scanf("%d",&j); -#endif - printf("enter 0 for \"off\", 1 for \"on\": "); - scanf("%d",&i); - if (i==0) j|=0x80; - printf("calling \"ioctl(drive,DDIOCSDBG,%d)\"\n",j); - rc=ioctl(drive,DDIOCSDBG,j); - printf("DDIOCSDBG: rc=%d.\n",rc); - break; - case 'Z': /* set the audio buffer size */ - printf("# frames wanted: ? "); - scanf("%d",&j); - rc=ioctl(drive,CDROMAUDIOBUFSIZ,j); - printf("%d frames granted.\n",rc); - break; -#endif SBP_PRIVATE_IOCTLS - default: - printf("unknown command: \"%s\".\n",command); - break; - } - } -} -/*==========================================================================*/ - diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.sjcd linux/drivers/block/README.sjcd --- v1.3.35/linux/drivers/block/README.sjcd Mon Oct 16 18:38:21 1995 +++ linux/drivers/block/README.sjcd Thu Jan 1 02:00:00 1970 @@ -1,80 +0,0 @@ - -- README.sjcd - 80% of the work takes 20% of the time, - 20% of the work takes 80% of the time... - (Murphy law) - - Once started, training can not be stopped... - (StarWars) - -This is the README for the sjcd cdrom driver, version 1.5. - -This file is meant as a tips & tricks edge for the usage of the SANYO CDR-H94A -cdrom drive. It will grow as the questions arise. ;-) -Since the drive often comes with an ISP16 soundcard, which can be used -as cdrom interface, this is also the place for ISP16 related issues. - -The driver should work with any SoundBlaster/Panasonic style CDROM interface, -including the "soft configurable" MediaMagic sound card. -To make this sound card (and others like "Mozart") working, it has to get -"configured" by software. -The suggestion to configure the ISP16 soundcard by booting DOS and -a warm reboot to boot Linux somehow doesn't work, at least not -on Eric's machine (IPC P90), with the version of the ISP16 -card he has (there appear to be at least two versions: Eric's card with -no jumpered IDE support and OPTi 82C928 chip, and Vadim's version -with a jumper to enable IDE support, probably with a OPTi 82C929 chip). -Therefore detection and configuration of the ISP16 interfaces is included -in the driver. -If we should support any other interfaces (which cannot be configured -through DOS) or if there are any more ISP16 types, please let us -know (maarel@marin.nl) and we'll see. - -Otherwise, you should boot DOS once (and by this, run the "configuration driver") -and then switch to Linux by use of CTRL-ALT-DEL. Each use of the RESET -button or the power switch makes this procedure necessary again. -If no ISP16 is detected, there's no harm done; a card configured trough DOS -may still work as expected. - -As of version 1.4 sound through the speakers is supported; only for MSS-mode -and no volume controle yet. - -PAUSE and STOP ioctl commands don't seem to work yet. - -ISP16 configuration routines reside at Vadim's server - ftp.rbrf.ru:/linux/mediamagic/ -and at Eberhard's mirror - ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/ - -Leo Spiekman's configuration routines for the ISP-16 card can get found at - dutette.et.tudelft.nl:/pub/linux/ -and at Eberhard's mirror - ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/ - -Eric van der Maarel's routines are included in sjcd.c. -This, and any related stuff may be found by anonymous ftp at - ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/ - -The device major for sjcd is 18, and minor is 0. Create a block special -file in your /dev directory (e.g., /dev/sjcd) with these numbers. -(For those who don't know, being root and doing the following should do the trick: - mknod -m 644 /dev/sjcd b 18 0 -and mount the cdrom by /dev/sjcd). - -The default configuration parameters are: - base address 0x340 - no irq - no dma -As of version 1.2, setting base address, irq and dma at boot time is supported -through the use of command line options: type at the "boot:" prompt: - linux sjcd=,, -(where your kernel is assumed to be called by saying "linux" to -the boot manager). - -If something is wrong, e-mail to vadim@rbrf.ru - or vadim@ipsun.ras.ru - or model@cecmow.enet.dec.com - -It happens sometimes that Vadim is not reachable by mail. For these -instances, Eric van der Maarel (maarel@marin.nl) will help, too. - - Vadim V. Model, Eric van der Maarel, Eberhard Moenkeberg diff -u --recursive --new-file v1.3.35/linux/drivers/block/README.sonycd535 linux/drivers/block/README.sonycd535 --- v1.3.35/linux/drivers/block/README.sonycd535 Fri Jul 7 08:54:44 1995 +++ linux/drivers/block/README.sonycd535 Thu Jan 1 02:00:00 1970 @@ -1,121 +0,0 @@ - README FOR LINUX SONY CDU-535/531 DRIVER - ======================================== - -This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux. -I do not think I have the documentation to add features like DMA support -so if anyone else wants to pursue it or help me with it, please do. -(I need to see what was done for the CDU-31A driver -- perhaps I can -steal some of that code.) - -This is a Linux device driver for the Sony CDU-535 CDROM drive. This is -one of the older Sony drives with its own interface card (Sony bus). -The DOS driver for this drive is named SONY_CDU.SYS - when you boot DOS -your drive should be identified as a SONY CDU-535. The driver works -with a CDU-531 also. One user reported that the driver worked on drives -OEM'ed by Procomm, drive and interface board were labelled Procomm. - -The Linux driver is based on Corey Minyard's sonycd 0.3 driver for -the CDU-31A. Ron Jeppesen just changed the commands that were sent -to the drive to correspond to the CDU-535 commands and registers. -There were enough changes to let bugs creep in but it seems to be stable. -Ron was able to tar an entire CDROM (should read all blocks) and built -ghostview and xfig off Walnut Creek's X11R5/GNU CDROM. xcdplayer and -workman work with the driver. Others have used the driver without -problems except those dealing with wait loops (fixed in third release). -Like Minyard's original driver this one uses a polled interface (this -is also the default setup for the DOS driver). It has not been tried -with interrupts or DMA enabled on the board. - -REQUIREMENTS -============ - - - Sony CDU-535 drive, preferably without interrupts and DMA - enabled on the card. - - - Drive must be set up as unit 1. Only the first unit will be - recognized - - - you must enter your interface address into - /usr/src/linux/include/linux/sonycd535.h and build the - appropriate kernel or use the "kernel command line" parameter - sonycd535=0x320 - with the correct interface address. - -NOTES: -====== - -1) The drive MUST be turned on when booting or it will not be recognized! - (but see comments on modularized version below) - -2) when the cdrom device is opened the eject button is disabled to keep the - user from ejecting a mounted disk and replacing it with another. - Unfortunately xcdplayer and workman also open the cdrom device so you - have to use the eject button in the software. Keep this in mind if your - cdrom player refuses to give up its disk -- exit workman or xcdplayer, or - umount the drive if it has been mounted. - -THANKS -====== - -Many thanks to Ron Jeppesen (ronj.an@site007.saic.com) for getting -this project off the ground. He wrote the initial release -and the first two patches to this driver (0.1, 0.2, and 0.3). -Thanks also to Eberhard Moenkeberg (emoenke@gwdg.de) for prodding -me to place this code into the mainstream Linux source tree -(as of Linux version 1.1.91), as well as some patches to make -it a better device citizen. Further thanks to "S. Joel Katz" - for his MODULE patches (see details below), -Porfiri Claudio for patches -to make the driver work with the older CDU-510/515 series, and -Heiko Eissfeldt 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 .) - - 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=
- - And to remove it: - -rmmod sony535 - - The code checks to see if MODULE is defined and behaves as it used -to if MODULE is not defined. That means your patched file should behave -exactly as it used to if compiled into the kernel. - - I have an external drive, and I usually leave it powered off. I used -to have to reboot if I needed to use the CDROM drive. Now I don't. - - Even if you have an internal drive, why waste the 268K of memory -(unswappable) that the driver uses if you use your CD-ROM drive infrequently? - - This driver will not install (whether compiled in or loaded as a -module) if the CDROM drive is not available during its initialization. This -means that you can have the driver compiled into the kernel and still load -the module later (assuming the driver doesn't install itself during -power-on). This only wastes 12K when you boot with the CDROM drive off. - - This is what I usually do; I leave the driver compiled into the -kernel, but load it as a module if I powered the system up with the drive -off and then later decided to use the CDROM drive. - - Since the driver only uses a single page to point to the chunks, -attempting to set the buffer cache to more than 2 Megabytes would be very -bad; don't do that. diff -u --recursive --new-file v1.3.35/linux/drivers/block/aztcd.c linux/drivers/block/aztcd.c --- v1.3.35/linux/drivers/block/aztcd.c Mon Oct 16 18:38:22 1995 +++ linux/drivers/block/aztcd.c Thu Jan 1 02:00:00 1970 @@ -1,2088 +0,0 @@ -#define AZT_VERSION "1.80" -/* $Id: aztcd.c,v 1.80 1995/10/11 19:35:03 root Exp root $ - linux/drivers/block/aztcd.c - AztechCD268 CDROM driver - - Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de) - - based on Mitsumi CDROM driver by Martin Hariss and preworks by - Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby - Schirmer. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - HISTORY - V0.0 Adaption to Adaptec CD268-01A Version 1.3 - Version is PRE_ALPHA, unresolved points: - 1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW - thus driver causes CPU overhead and is very slow - 2. could not find a way to stop the drive, when it is - in data read mode, therefore I had to set - msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one - frame can be read in sequence, this is also the reason for - 3. getting 'timeout in state 4' messages, but nevertheless - it works - W.Zimmermann, Oct. 31, 1994 - V0.1 Version is ALPHA, problems #2 and #3 resolved. - W.Zimmermann, Nov. 3, 1994 - V0.2 Modification to some comments, debugging aids for partial test - with Borland C under DOS eliminated. Timer interrupt wait - STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented; - use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_ - SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy - waiting seems better to me than interrupt rescheduling. - Besides that, when used in the wrong place, STEN_LOW_WAIT causes - kernel panic. - In function aztPlay command ACMD_PLAY_AUDIO added, should make - audio functions work. The Aztech drive needs different commands - to read data tracks and play audio tracks. - W.Zimmermann, Nov. 8, 1994 - V0.3 Recognition of missing drive during boot up improved (speeded up). - W.Zimmermann, Nov. 13, 1994 - V0.35 Rewrote the control mechanism in azt_poll (formerly mcd_poll) - including removal of all 'goto' commands. :-); - J. Nardone, Nov. 14, 1994 - V0.4 Renamed variables and constants to 'azt' instead of 'mcd'; had - to make some "compatibility" defines in azt.h; please note, - that the source file was renamed to azt.c, the include file to - azt.h - Speeded up drive recognition during init (will be a little bit - slower than before if no drive is installed!); suggested by - Robby Schirmer. - read_count declared volatile and set to AZT_BUF_SIZ to make - drive faster (now 300kB/sec, was 60kB/sec before, measured - by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096'; - different AZT_BUF_SIZes were test, above 16 no further im- - provement seems to be possible; suggested by E.Moenkeberg. - W.Zimmermann, Nov. 18, 1994 - V0.42 Included getAztStatus command in GetQChannelInfo() to allow - reading Q-channel info on audio disks, if drive is stopped, - and some other bug fixes in the audio stuff, suggested by - Robby Schirmer. - Added more ioctls (reading data in mode 1 and mode 2). - Completely removed the old azt_poll() routine. - Detection of ORCHID CDS-3110 in aztcd_init implemented. - Additional debugging aids (see the readme file). - W.Zimmermann, Dec. 9, 1994 - V0.50 Autodetection of drives implemented. - W.Zimmermann, Dec. 12, 1994 - V0.52 Prepared for including in the standard kernel, renamed most - variables to contain 'azt', included autoconf.h - W.Zimmermann, Dec. 16, 1994 - V0.6 Version for being included in the standard Linux kernel. - Renamed source and header file to aztcd.c and aztcd.h - W.Zimmermann, Dec. 24, 1994 - V0.7 Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case - CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl, - which causes kernel crashes when playing audio, changed - include-files (config.h instead of autoconf.h, removed - delay.h) - W.Zimmermann, Jan. 8, 1995 - V0.72 Some more modifications for adaption to the standard kernel. - W.Zimmermann, Jan. 16, 1995 - V0.80 aztcd is now part of the standard kernel since version 1.1.83. - Modified the SET_TIMER and CLEAR_TIMER macros to comply with - the new timer scheme. - W.Zimmermann, Jan. 21, 1995 - V0.90 Included CDROMVOLCTRL, but with my Aztech drive I can only turn - the channels on and off. If it works better with your drive, - please mail me. Also implemented ACMD_CLOSE for CDROMSTART. - W.Zimmermann, Jan. 24, 1995 - V1.00 Implemented close and lock tray commands. Patches supplied by - Frank Racis - Added support for loadable MODULEs, so aztcd can now also be - loaded by insmod and removed by rmmod during run time - Werner Zimmermann, Mar. 24, 95 - V1.10 Implemented soundcard configuration for Orchid CDS-3110 drives - connected to Soundwave32 cards. Release for LST 2.1. - (still experimental) - Werner Zimmermann, May 8, 95 - V1.20 Implemented limited support for DOSEMU0.60's cdrom.c. Now it works, but - sometimes DOSEMU may hang for 30 seconds or so. A fully functional ver- - sion needs an update of Dosemu0.60's cdrom.c, which will come with the - next revision of Dosemu. - Also Soundwave32 support now works. - Werner Zimmermann, May 22, 95 - V1.30 Auto-eject feature. Inspired by Franc Racis (racis@psu.edu) - Werner Zimmermann, July 4, 95 - V1.40 Started multisession support. Implementation copied from mcdx.c - by Heiko Schlittermann. Not tested yet. - Werner Zimmermann, July 15, 95 - V1.50 Implementation of ioctl CDROMRESET, continued multisession, began - XA, but still untested. Heavy modifications to drive status de- - tection. - Werner Zimmermann, July 25, 95 - V1.60 XA support now should work. Speeded up drive recognition in cases, - where no drive is installed. - Werner Zimmermann, August 8, 1995 - V1.70 Multisession support now is completed, but there is still not - enough testing done. If you can test it, please contact me. For - details please read README.aztcd. - Werner Zimmermann, August 19, 1995 - V1.80 Modification to suit the new kernel boot procedure introduced - with kernel 1.3.33. Will definitely not work with older kernels. - Programming done by Linus himself. - Werner Zimmermann, October 11, 1995 - NOTE: - Points marked with ??? are questionable ! -*/ -#include -#include - -#ifdef MODULE -# include -# include -# ifndef CONFIG_MODVERSIONS - char kernel_version[]= UTS_RELEASE; -# endif -#define aztcd_init init_module -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MAJOR_NR AZTECH_CDROM_MAJOR - -#include "blk.h" - -#ifdef MODULE -#else -# define MOD_INC_USE_COUNT -# define MOD_DEC_USE_COUNT -#endif - -#include - -#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 /* */ -#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) - { 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 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 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) - { 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 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; ii_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=,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,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;st0;limit--) - { if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetDiskInfo 2",-1); - if (qInfo.pointIndex==0xA0) /*Number of FirstTrack*/ - { DiskInfo.first = qInfo.diskTime.min; - DiskInfo.first = azt_bcd2bin(DiskInfo.first); - test=test|0x01; - } - if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/ - { DiskInfo.last = qInfo.diskTime.min; - DiskInfo.last = azt_bcd2bin(DiskInfo.last); - test=test|0x02; - } - if (qInfo.pointIndex==0xA2) /*DiskLength*/ - { DiskInfo.diskLength.min=qInfo.diskTime.min; - DiskInfo.diskLength.sec=qInfo.diskTime.sec; - DiskInfo.diskLength.frame=qInfo.diskTime.frame; - test=test|0x04; - } - if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01)) /*StartTime of First Track*/ - { DiskInfo.firstTrack.min=qInfo.diskTime.min; - DiskInfo.firstTrack.sec=qInfo.diskTime.sec; - DiskInfo.firstTrack.frame=qInfo.diskTime.frame; - test=test|0x08; - } - if (test==0x0F) break; - } -#ifdef AZT_DEBUG - printk ("aztcd: exiting aztGetDiskInfo Time:%li\n",jiffies); - printk("Disk Info: first %d last %d length %02X:%02X.%02X dez first %02X:%02X.%02X dez\n", - DiskInfo.first, - DiskInfo.last, - DiskInfo.diskLength.min, - DiskInfo.diskLength.sec, - DiskInfo.diskLength.frame, - DiskInfo.firstTrack.min, - DiskInfo.firstTrack.sec, - DiskInfo.firstTrack.frame); -#endif - if (test!=0x0F) return -1; - return 0; -} - -#if AZT_MULTISESSION -/* - * Get Multisession Disk Info - */ -static int aztGetMultiDiskInfo(void) -{ int limit, k=5; - unsigned char test; - struct azt_Toc qInfo; - -#ifdef AZT_DEBUG - printk("aztcd: starting aztGetMultiDiskInfo\n"); -#endif - - do { azt_Play.start.min = Toc[DiskInfo.last+1].diskTime.min; - azt_Play.start.sec = Toc[DiskInfo.last+1].diskTime.sec; - azt_Play.start.frame = Toc[DiskInfo.last+1].diskTime.frame; - test=0; - - for (limit=30;limit>0;limit--) /*Seek for LeadIn of next session*/ - { if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 1",-1); - if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 2",-1); - if ((qInfo.track==0)&&(qInfo.pointIndex)) break; /*LeadIn found*/ - if ((azt_Play.start.sec+=10) > 59) - { azt_Play.start.sec=0; - azt_Play.start.min++; - } - } - if (!limit) break; /*Check, if a leadin track was found, if not we're - at the end of the disk*/ -#ifdef AZT_DEBUG_MULTISESSION - printk("leadin found track %d pointIndex %x limit %d\n",qInfo.track,qInfo.pointIndex,limit); -#endif - for (limit=300;limit>0;limit--) - { if (++azt_Play.start.frame>74) - { azt_Play.start.frame=0; - if (azt_Play.start.sec > 59) - { azt_Play.start.sec=0; - azt_Play.start.min++; - } - } - if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 3",-1); - if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 4",-1); - if (qInfo.pointIndex==0xA0) /*Number of NextTrack*/ - { DiskInfo.next = qInfo.diskTime.min; - DiskInfo.next = azt_bcd2bin(DiskInfo.next); - test=test|0x01; - } - if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/ - { DiskInfo.last = qInfo.diskTime.min; - DiskInfo.last = azt_bcd2bin(DiskInfo.last); - test=test|0x02; - } - if (qInfo.pointIndex==0xA2) /*DiskLength*/ - { DiskInfo.diskLength.min =qInfo.diskTime.min; - DiskInfo.diskLength.sec =qInfo.diskTime.sec; - DiskInfo.diskLength.frame=qInfo.diskTime.frame; - test=test|0x04; - } - if ((qInfo.pointIndex==DiskInfo.next)&&(test&0x01)) /*StartTime of Next Track*/ - { DiskInfo.nextSession.min=qInfo.diskTime.min; - DiskInfo.nextSession.sec=qInfo.diskTime.sec; - DiskInfo.nextSession.frame=qInfo.diskTime.frame; - test=test|0x08; - } - if (test==0x0F) break; - } -#ifdef AZT_DEBUG_MULTISESSION - printk ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez first %02x:%02x.%02x dez next %02x:%02x.%02x dez\n", - DiskInfo.first, - DiskInfo.next, - DiskInfo.last, - DiskInfo.diskLength.min, - DiskInfo.diskLength.sec, - DiskInfo.diskLength.frame, - DiskInfo.firstTrack.min, - DiskInfo.firstTrack.sec, - DiskInfo.firstTrack.frame, - DiskInfo.nextSession.min, - DiskInfo.nextSession.sec, - DiskInfo.nextSession.frame); -#endif - if (test!=0x0F) - break; - else - DiskInfo.multi=1; /*found TOC of more than one session*/ - aztGetToc(1); - } while(--k); - -#ifdef AZT_DEBUG - printk ("aztcd: exiting aztGetMultiDiskInfo Time:%li\n",jiffies); -#endif - return 0; -} -#endif - -/* - * Read the table of contents (TOC) - */ -static int aztGetToc(int multi) -{ int i, px; - int limit; - struct azt_Toc qInfo; - -#ifdef AZT_DEBUG - printk("aztcd: starting aztGetToc Time:%li\n",jiffies); -#endif - if (!multi) - { for (i = 0; i < MAX_TRACKS; i++) - Toc[i].pointIndex = 0; - i = DiskInfo.last + 3; - } - else - { for (i = DiskInfo.next; i < MAX_TRACKS; i++) - Toc[i].pointIndex = 0; - i = DiskInfo.last + 4 - DiskInfo.next; - } - -/*Is there a good reason to stop motor before TOC read? - if (aztSendCmd(ACMD_STOP)) RETURNM("aztGetToc 1",-1); - STEN_LOW_WAIT; -*/ - - if (!multi) - { azt_mode = 0x05; - if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1); /*???*/ - STEN_LOW_WAIT; - } - for (limit = 300; limit > 0; limit--) - { if (multi) - { if (++azt_Play.start.sec > 59) - { azt_Play.start.sec=0; - azt_Play.start.min++; - } - if (aztSeek(&azt_Play)) RETURNM("aztGetToc 3",-1); - } - if (aztGetQChannelInfo(&qInfo) < 0) - break; - - px = azt_bcd2bin(qInfo.pointIndex); - - if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) - if (Toc[px].pointIndex == 0) - { Toc[px] = qInfo; - i--; - } - - if (i <= 0) - break; - } - - Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; - - -#ifdef AZT_DEBUG_MULTISESSION - printk("aztcd: exiting aztGetToc\n"); - for (i = 1; i <= DiskInfo.last+1; i++) - printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", - i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, - Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, - Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); - for (i = 100; i < 103; i++) - printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", - i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, - Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, - Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); -#endif - - return limit > 0 ? 0 : -1; -} - -#ifdef MODULE -void cleanup_module(void) -{ if (MOD_IN_USE) - { printk("aztcd module in use - can't remove it.\n"); - return; - } - if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) - { printk("What's that: can't unregister aztcd\n"); - return; - } - release_region(azt_port,4); - printk("aztcd module released.\n"); -} -#endif MODULE diff -u --recursive --new-file v1.3.35/linux/drivers/block/blk.h linux/drivers/block/blk.h --- v1.3.35/linux/drivers/block/blk.h Fri Oct 13 14:44:32 1995 +++ linux/drivers/block/blk.h Thu Jan 1 02:00:00 1970 @@ -1,393 +0,0 @@ -#ifndef _BLK_H -#define _BLK_H - -#include -#include -#include - -/* - * 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<rq_dev) != MAJOR_NR) \ - panic(DEVICE_NAME ": request list destroyed"); \ - if (CURRENT->bh) { \ - if (!CURRENT->bh->b_lock) \ - panic(DEVICE_NAME ": block not locked"); \ - } - -#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) */ - -/* end_request() - SCSI devices have their own version */ -/* - IDE drivers have their own copy too */ - -#if ! SCSI_MAJOR(MAJOR_NR) - -#if defined(_IDE_CD_C) || defined(_TRITON_C) /* shares copy with ide.c */ -void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup); -#else - -#ifdef IDE_DRIVER -void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) { - struct request *req = hwgroup->rq; -#else -static void end_request(int uptodate) { - struct request *req = CURRENT; -#endif /* IDE_DRIVER */ - struct buffer_head * bh; - - req->errors = 0; - if (!uptodate) { - printk("end_request: I/O error, dev %s, sector %lu\n", - kdevname(req->rq_dev), req->sector); - req->nr_sectors--; - req->nr_sectors &= ~SECTOR_MASK; - req->sector += (BLOCK_SIZE / 512); - req->sector &= ~SECTOR_MASK; - } - - if ((bh = req->bh) != NULL) { - req->bh = bh->b_reqnext; - bh->b_reqnext = NULL; - bh->b_uptodate = uptodate; - unlock_buffer(bh); - if ((bh = req->bh) != NULL) { - req->current_nr_sectors = bh->b_size >> 9; - if (req->nr_sectors < req->current_nr_sectors) { - req->nr_sectors = req->current_nr_sectors; - printk("end_request: buffer-list destroyed\n"); - } - req->buffer = bh->b_data; - return; - } - } -#ifdef IDE_DRIVER - hwgroup->rq = NULL; -#else - DEVICE_OFF(req->rq_dev); - CURRENT = req->next; -#endif /* IDE_DRIVER */ - if (req->sem != NULL) - up(req->sem); - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); -} -#endif /* ndef _IDE_CD_C */ -#endif /* ! SCSI_MAJOR(MAJOR_NR) */ - -#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */ - -#endif /* _BLK_H */ diff -u --recursive --new-file v1.3.35/linux/drivers/block/cdu31a.c linux/drivers/block/cdu31a.c --- v1.3.35/linux/drivers/block/cdu31a.c Tue Oct 10 18:46:31 1995 +++ linux/drivers/block/cdu31a.c Thu Jan 1 02:00:00 1970 @@ -1,3104 +0,0 @@ -/* - * Sony CDU-31A CDROM interface device driver. - * - * Corey Minyard (minyard@wf-rch.cirr.com) - * - * Colossians 3:17 - * - * The Sony interface device driver handles Sony interface CDROM - * drives and provides a complete block-level interface as well as an - * ioctl() interface compatible with the Sun (as specified in - * include/linux/cdrom.h). With this interface, CDROMs can be - * accessed and standard audio CDs can be played back normally. - * - * WARNING - All autoprobes have been removed from the driver. - * You MUST configure the CDU31A via a LILO config - * at boot time or in lilo.conf. I have the - * following in my lilo.conf: - * - * append="cdu31a=0x1f88,0,PAS" - * - * The first number is the I/O base address of the - * card. The second is the interrupt (0 means none). - * The third should be "PAS" if on a Pro-Audio - * spectrum, or nothing if on something else. - * - * This interface is (unfortunately) a polled interface. This is - * because most Sony interfaces are set up with DMA and interrupts - * disables. Some (like mine) do not even have the capability to - * handle interrupts or DMA. For this reason you will see a lot of - * the following: - * - * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT; - * while ((retry_count > jiffies) && (! -#include - -#ifdef MODULE -# include -# include -# ifndef CONFIG_MODVERSIONS - char kernel_version[]= UTS_RELEASE; -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#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 readahead_dataleft) - { - /* The readahead will not fill the requested buffer, but - get the data out of the readahead into the buffer. */ - memcpy(buffer, - readahead_buffer + (2048 - readahead_dataleft), - readahead_dataleft); - readahead_dataleft = 0; - bytesleft -= readahead_dataleft; - offset += readahead_dataleft; - } - else - { - /* The readahead will fill the whole buffer, get the data - and return. */ - memcpy(buffer, - readahead_buffer + (2048 - readahead_dataleft), - bytesleft); - readahead_dataleft -= bytesleft; - bytesleft = 0; - sony_blocks_left -= nblocks; - sony_next_block += nblocks; - - /* If the data in the readahead is bad, return an error so the - driver will abort the buffer. */ - if (readahead_bad) - { - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - - if (readahead_dataleft == 0) - { - readahead_bad = 0; - } - - /* Final transfer is done for read command, get final result. */ - if (sony_blocks_left == 0) - { - get_result(res_reg, res_size); - } - return; - } - } - - /* Wait for the drive to tell us we have something */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while ((retry_count > jiffies) && !(is_data_ready())) - { - while (handle_sony_cd_attention()) - ; - - sony_sleep(); - } - if (!(is_data_ready())) - { - if (is_result_ready()) - { - get_result(res_reg, res_size); - if ((res_reg[0] & 0xf0) != 0x20) - { - printk("CDU31A: Got result that should have been error: %d\n", - res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - abort_read(); - } - else - { -#if DEBUG - printk("CDU31A timeout out %d\n", __LINE__); -#endif - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - } - } - else - { - input_data(buffer, bytesleft, nblocks, offset, skip); - - /* Wait for the status from the drive. */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while ((retry_count > jiffies) && !(is_result_ready())) - { - while (handle_sony_cd_attention()) - ; - - sony_sleep(); - } - - if (!is_result_ready()) - { -#if DEBUG - printk("CDU31A timeout out %d\n", __LINE__); -#endif - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - } - else - { - get_result(res_reg, res_size); - - /* If we got a buffer status, handle that. */ - if ((res_reg[0] & 0xf0) == 0x50) - { - - if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT) - || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT) - || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT)) - { - /* The data was successful, but if data was read from - the readahead and it was bad, set the whole - buffer as bad. */ - if (readahead_bad) - { - readahead_bad = 0; - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - } - else - { - printk("CDU31A: Data block error: 0x%x\n", res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - - /* Data is in the readahead buffer but an error was returned. - Make sure future requests don't use the data. */ - if (bytesleft != 2048) - { - readahead_bad = 1; - } - } - - /* Final transfer is done for read command, get final result. */ - if (sony_blocks_left == 0) - { - get_result(res_reg, res_size); - } - } - else if ((res_reg[0] & 0xf0) != 0x20) - { - /* The drive gave me bad status, I don't know what to do. - Reset the driver and return an error. */ - printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]); - restart_on_error(); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - } - } -} - -/* - * The OS calls this to perform a read or write operation to the drive. - * Write obviously fail. Reads to a read ahead of sony_buffer_size - * bytes to help speed operations. This especially helps since the OS - * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most - * data access on a CD is done sequentially, this saves a lot of operations. - */ -static void -do_cdu31a_request(void) -{ - int block; - int nblock; - unsigned char res_reg[12]; - unsigned int res_size; - int num_retries; - unsigned long flags; - - - /* - * Make sure no one else is using the driver; wait for them - * to finish if it is so. - */ - save_flags(flags); - cli(); - while (sony_inuse) - { - interruptible_sleep_on(&sony_wait); - if (current->signal & ~current->blocked) - { - restore_flags(flags); - if (CURRENT && CURRENT->rq_status != RQ_INACTIVE) - { - end_request(0); - } - restore_flags(flags); - return; - } - } - sony_inuse = 1; - has_cd_task = current; - - /* Get drive status before doing anything. */ - while (handle_sony_cd_attention()) - ; - - sti(); - - /* If the timer is running, cancel it. */ - if (cdu31a_abort_timer.next != NULL) - { - del_timer(&cdu31a_abort_timer); - } - - while (1) - { -cdu31a_request_startover: - /* - * The beginning here is stolen from the hard disk driver. I hope - * it's right. - */ - if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) - { - goto end_do_cdu31a_request; - } - - if (!sony_spun_up) - { - struct inode in; - - /* This is a kludge to get a valid dev in an inode that - scd_open can take. That's the only thing scd_open() - uses the inode for. */ - in.i_rdev = CURRENT->rq_dev; - scd_open(&in,NULL); - } - - /* I don't use INIT_REQUEST because it calls return, which would - return without unlocking the device. It shouldn't matter, - but just to be safe... */ - if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) - { - panic(DEVICE_NAME ": request list destroyed"); - } - if (CURRENT->bh) - { - if (!CURRENT->bh->b_lock) - { - panic(DEVICE_NAME ": block not locked"); - } - } - - block = CURRENT->sector; - nblock = CURRENT->nr_sectors; - - if (!sony_toc_read) - { - printk("CDU31A: TOC not read\n"); - end_request(0); - goto cdu31a_request_startover; - } - - /* Check for base read of multi-session disk. This will still work - for single session disks, so just do it. Blocks less than 80 - are for the volume info, so offset them by the start track (which - should be zero for a single-session disk). */ - if (block < 80) - { - /* Offset the request into the session. */ - block += (sony_toc.start_track_lba * 4); - } - - switch(CURRENT->cmd) - { - case READ: - /* - * If the block address is invalid or the request goes beyond the end of - * the media, return an error. - */ -#if 0 - if ((block / 4) < sony_toc.start_track_lba) - { - printk("CDU31A: Request before beginning of media\n"); - end_request(0); - goto cdu31a_request_startover; - } -#endif - if ((block / 4) >= sony_toc.lead_out_start_lba) - { - printk("CDU31A: Request past end of media\n"); - end_request(0); - goto cdu31a_request_startover; - } - if (((block + nblock) / 4) >= sony_toc.lead_out_start_lba) - { - printk("CDU31A: Request past end of media\n"); - end_request(0); - goto cdu31a_request_startover; - } - - num_retries = 0; - -try_read_again: - while (handle_sony_cd_attention()) - ; - - if (!sony_toc_read) - { - printk("CDU31A: TOC not read\n"); - end_request(0); - goto cdu31a_request_startover; - } - - /* If no data is left to be read from the drive, start the - next request. */ - if (sony_blocks_left == 0) - { - if (start_request(block / 4, CDU31A_READAHEAD / 4, 0)) - { - end_request(0); - goto cdu31a_request_startover; - } - } - /* If the requested block is not the next one waiting in - the driver, abort the current operation and start a - new one. */ - else if (block != sony_next_block) - { -#if DEBUG - printk("CDU31A Warning: Read for block %d, expected %d\n", - block, - sony_next_block); -#endif - abort_read(); - if (!sony_toc_read) - { - printk("CDU31A: TOC not read\n"); - end_request(0); - goto cdu31a_request_startover; - } - if (start_request(block / 4, CDU31A_READAHEAD / 4, 0)) - { - printk("CDU31a: start request failed\n"); - end_request(0); - goto cdu31a_request_startover; - } - } - - read_data_block(CURRENT->buffer, block, nblock, res_reg, &res_size); - if (res_reg[0] == 0x20) - { - if (num_retries > MAX_CDU31A_RETRIES) - { - end_request(0); - goto cdu31a_request_startover; - } - - num_retries++; - if (res_reg[1] == SONY_NOT_SPIN_ERR) - { - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - } - else - { - printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]); - } - goto try_read_again; - } - else - { - end_request(1); - } - break; - - case WRITE: - end_request(0); - break; - - default: - panic("CDU31A: Unknown cmd"); - } - } - -end_do_cdu31a_request: -#if 0 - /* After finished, cancel any pending operations. */ - abort_read(); -#else - /* Start a timer to time out after a while to disable - the read. */ - cdu31a_abort_timer.expires = jiffies + 2*HZ; /* Wait 2 seconds */ - add_timer(&cdu31a_abort_timer); -#endif - - has_cd_task = NULL; - sony_inuse = 0; - wake_up_interruptible(&sony_wait); - restore_flags(flags); -} - -/* Copy overlapping buffers. */ -static void -mcovlp(char *dst, - char *src, - int size) -{ - src += (size - 1); - dst += (size - 1); - while (size > 0) - { - *dst = *src; - size--; - dst--; - src--; - } -} - - -/* - * Read the table of contents from the drive and set up TOC if - * successful. - */ -static void -sony_get_toc(void) -{ - unsigned char res_reg[2]; - unsigned int res_size; - unsigned char parms[1]; - int session; - - -#if DEBUG - printk("Entering sony_get_toc\n"); -#endif - - if (!sony_toc_read) - { - /* The idea here is we keep asking for sessions until the command - fails. Then we know what the last valid session on the disk is. - No need to check session 0, since session 0 is the same as session - 1; the command returns different information if you give it 0. - Don't check session 1 because that is the first session, it must - be there. */ - session = 2; - while (1) - { -#if DEBUG - printk("Trying session %d\n", session); -#endif - parms[0] = session; - do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD, - parms, - 1, - res_reg, - &res_size); - -#if DEBUG - printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]); -#endif - - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) - { - /* An error reading the TOC, this must be past the last session. */ - break; - } - - session++; - - /* Let's not get carried away... */ - if (session > 20) - { - return; - } - } - - session--; - -#if DEBUG - printk("Reading session %d\n", session); -#endif - - parms[0] = session; - do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD, - parms, - 1, - (unsigned char *) &sony_toc, - &res_size); - if ((res_size < 2) || ((sony_toc.exec_status[0] & 0xf0) == 0x20)) - { - /* An error reading the TOC. Return without sony_toc_read - set. */ - return; - } - - sony_toc_read = 1; - - /* For points that do not exist, move the data over them - to the right location. */ - if (sony_toc.pointb0 != 0xb0) - { - mcovlp(((char *) &sony_toc) + 27, - ((char *) &sony_toc) + 18, - res_size - 18); - res_size += 9; - } - if (sony_toc.pointb1 != 0xb1) - { - mcovlp(((char *) &sony_toc) + 36, - ((char *) &sony_toc) + 27, - res_size - 27); - res_size += 9; - } - if (sony_toc.pointb2 != 0xb2) - { - mcovlp(((char *) &sony_toc) + 45, - ((char *) &sony_toc) + 36, - res_size - 36); - res_size += 9; - } - if (sony_toc.pointb3 != 0xb3) - { - mcovlp(((char *) &sony_toc) + 54, - ((char *) &sony_toc) + 45, - res_size - 45); - res_size += 9; - } - if (sony_toc.pointb4 != 0xb4) - { - mcovlp(((char *) &sony_toc) + 63, - ((char *) &sony_toc) + 54, - res_size - 54); - res_size += 9; - } - if (sony_toc.pointc0 != 0xc0) - { - mcovlp(((char *) &sony_toc) + 72, - ((char *) &sony_toc) + 63, - res_size - 63); - res_size += 9; - } - - sony_toc.start_track_lba = msf_to_log(sony_toc.tracks[0].track_start_msf); - sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf); - -#if DEBUG - printk("Disk session %d, start track: %d, stop track: %d\n", - session, - sony_toc.start_track_lba, - sony_toc.lead_out_start_lba); -#endif - } -#if DEBUG - printk("Leaving sony_get_toc\n"); -#endif -} - - -/* - * Search for a specific track in the table of contents. - */ -static int -find_track(int track) -{ - int i; - int num_tracks; - - - num_tracks = sony_toc.last_track_num - sony_toc.first_track_num + 1; - for (i = 0; i < num_tracks; i++) - { - if (sony_toc.tracks[i].track == track) - { - return i; - } - } - - return -1; -} - - -/* - * Read the subcode and put it int last_sony_subcode for future use. - */ -static int -read_subcode(void) -{ - unsigned int res_size; - - - do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD, - NULL, - 0, - (unsigned char *) &last_sony_subcode, - &res_size); - if ((res_size < 2) || ((last_sony_subcode.exec_status[0] & 0xf0) == 0x20)) - { - printk("Sony CDROM error 0x%2.2x (read_subcode)\n", - last_sony_subcode.exec_status[1]); - return -EIO; - } - - return 0; -} - - -/* - * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If - * the drive is playing, the subchannel needs to be read (since it would be - * changing). If the drive is paused or completed, the subcode information has - * already been stored, just use that. The ioctl call wants things in decimal - * (not BCD), so all the conversions are done. - */ -static int -sony_get_subchnl_info(long arg) -{ - struct cdrom_subchnl schi; - - - /* Get attention stuff */ - while (handle_sony_cd_attention()) - ; - - sony_get_toc(); - if (!sony_toc_read) - { - return -EIO; - } - - verify_area(VERIFY_READ, (char *) arg, sizeof(schi)); - verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi)); - - memcpy_fromfs(&schi, (char *) arg, sizeof(schi)); - - switch (sony_audio_status) - { - case CDROM_AUDIO_PLAY: - if (read_subcode() < 0) - { - return -EIO; - } - break; - - case CDROM_AUDIO_PAUSED: - case CDROM_AUDIO_COMPLETED: - break; - - case CDROM_AUDIO_NO_STATUS: - schi.cdsc_audiostatus = sony_audio_status; - memcpy_tofs((char *) arg, &schi, sizeof(schi)); - return 0; - break; - - case CDROM_AUDIO_INVALID: - case CDROM_AUDIO_ERROR: - default: - return -EIO; - } - - schi.cdsc_audiostatus = sony_audio_status; - schi.cdsc_adr = last_sony_subcode.address; - schi.cdsc_ctrl = last_sony_subcode.control; - schi.cdsc_trk = bcd_to_int(last_sony_subcode.track_num); - schi.cdsc_ind = bcd_to_int(last_sony_subcode.index_num); - if (schi.cdsc_format == CDROM_MSF) - { - schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode.abs_msf[0]); - schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode.abs_msf[1]); - schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode.abs_msf[2]); - - schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode.rel_msf[0]); - schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode.rel_msf[1]); - schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode.rel_msf[2]); - } - else if (schi.cdsc_format == CDROM_LBA) - { - schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf); - schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf); - } - - memcpy_tofs((char *) arg, &schi, sizeof(schi)); - return 0; -} - -/* Get audio data from the drive. This is fairly complex because I - am looking for status and data at the same time, but if I get status - then I just look for data. I need to get the status immediately so - the switch from audio to data tracks will happen quickly. */ -static void -read_audio_data(char *buffer, - unsigned char res_reg[], - int *res_size) -{ - unsigned int retry_count; - int result_read; - - - res_reg[0] = 0; - res_reg[1] = 0; - *res_size = 0; - result_read = 0; - - /* Wait for the drive to tell us we have something */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; -continue_read_audio_wait: - while ( (retry_count > jiffies) - && !(is_data_ready()) - && !(is_result_ready() || result_read)) - { - while (handle_sony_cd_attention()) - ; - - sony_sleep(); - } - if (!(is_data_ready())) - { - if (is_result_ready() && !result_read) - { - get_result(res_reg, res_size); - - /* Read block status and continue waiting for data. */ - if ((res_reg[0] & 0xf0) == 0x50) - { - result_read = 1; - goto continue_read_audio_wait; - } - /* Invalid data from the drive. Shut down the operation. */ - else if ((res_reg[0] & 0xf0) != 0x20) - { - printk("CDU31A: Got result that should have been error: %d\n", - res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - abort_read(); - } - else - { -#if DEBUG - printk("CDU31A timeout out %d\n", __LINE__); -#endif - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - } - } - else - { - clear_data_ready(); - - /* If data block, then get 2340 bytes offset by 12. */ - if (sony_raw_data_mode) - { - insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_XA); - } - else - { - /* Audio gets the whole 2352 bytes. */ - insb(sony_cd_read_reg, buffer, CD_FRAMESIZE_RAW); - } - - /* If I haven't already gotten the result, get it now. */ - if (!result_read) - { - /* Wait for the drive to tell us we have something */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while ((retry_count > jiffies) && !(is_result_ready())) - { - while (handle_sony_cd_attention()) - ; - - sony_sleep(); - } - - if (!is_result_ready()) - { -#if DEBUG - printk("CDU31A timeout out %d\n", __LINE__); -#endif - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - return; - } - else - { - get_result(res_reg, res_size); - } - } - - if ((res_reg[0] & 0xf0) == 0x50) - { - if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT) - || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT) - || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT) - || (res_reg[0] == SONY_NO_ERR_DETECTION_STAT)) - { - /* Ok, nothing to do. */ - } - else - { - printk("CDU31A: Data block error: 0x%x\n", res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - } - else if ((res_reg[0] & 0xf0) != 0x20) - { - /* The drive gave me bad status, I don't know what to do. - Reset the driver and return an error. */ - printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]); - restart_on_error(); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - } -} - -/* Perform a raw data read. This will automatically detect the - track type and read the proper data (audio or data). */ -static int -read_audio(struct cdrom_read_audio *ra, - struct inode *inode) -{ - int retval; - unsigned char params[2]; - unsigned char res_reg[12]; - unsigned int res_size; - unsigned int cframe; - unsigned long flags; - - /* - * Make sure no one else is using the driver; wait for them - * to finish if it is so. - */ - save_flags(flags); - cli(); - while (sony_inuse) - { - interruptible_sleep_on(&sony_wait); - if (current->signal & ~current->blocked) - { - restore_flags(flags); - return -EAGAIN; - } - } - sony_inuse = 1; - has_cd_task = current; - restore_flags(flags); - - if (!sony_spun_up) - { - scd_open (inode, NULL); - } - - /* Set the drive to do raw operations. */ - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x06 | sony_raw_data_mode; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) - { - printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]); - return -EIO; - } - - /* From here down, we have to goto exit_read_audio instead of returning - because the drive parameters have to be set back to data before - return. */ - - retval = 0; - /* start_request clears out any readahead data, so it should be safe. */ - if (start_request(ra->addr.lba, ra->nframes, 1)) - { - retval = -EIO; - goto exit_read_audio; - } - - /* For every requested frame. */ - cframe = 0; - while (cframe < ra->nframes) - { - read_audio_data(readahead_buffer, res_reg, &res_size); - if ((res_reg[0] & 0xf0) == 0x20) - { - if (res_reg[1] == SONY_BAD_DATA_ERR) - { - printk("CDU31A: Data error on audio sector %d\n", - ra->addr.lba + cframe); - } - else if (res_reg[1] == SONY_ILL_TRACK_R_ERR) - { - /* Illegal track type, change track types and start over. */ - sony_raw_data_mode = (sony_raw_data_mode) ? 0 : 1; - - /* Set the drive mode. */ - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x06 | sony_raw_data_mode; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) - { - printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]); - retval = -EIO; - goto exit_read_audio; - } - - /* Restart the request on the current frame. */ - if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1)) - { - retval = -EIO; - goto exit_read_audio; - } - - /* Don't go back to the top because don't want to get into - and infinite loop. A lot of code gets duplicated, but - that's no big deal, I don't guess. */ - read_audio_data(readahead_buffer, res_reg, &res_size); - if ((res_reg[0] & 0xf0) == 0x20) - { - if (res_reg[1] == SONY_BAD_DATA_ERR) - { - printk("CDU31A: Data error on audio sector %d\n", - ra->addr.lba + cframe); - } - else - { - printk("CDU31A: Error reading audio data on sector %d: 0x%x\n", - ra->addr.lba + cframe, - res_reg[1]); - retval = -EIO; - goto exit_read_audio; - } - } - else - { - memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)), - (char *) readahead_buffer, - CD_FRAMESIZE_RAW); - } - } - else - { - printk("CDU31A: Error reading audio data on sector %d: 0x%x\n", - ra->addr.lba + cframe, - res_reg[1]); - retval = -EIO; - goto exit_read_audio; - } - } - else - { - memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)), - (char *) readahead_buffer, - CD_FRAMESIZE_RAW); - } - - cframe++; - } - - get_result(res_reg, &res_size); - if ((res_reg[0] & 0xf0) == 0x20) - { - printk("CDU31A: Error return from audio read: 0x%x\n", - res_reg[1]); - retval = -EIO; - goto exit_read_audio; - } - -exit_read_audio: - - /* Set the drive mode back to the proper one for the disk. */ - params[0] = SONY_SD_DECODE_PARAM; - if (!sony_xa_mode) - { - params[1] = 0x0f; - } - else - { - params[1] = 0x07; - } - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) - { - printk("CDU31A: Unable to reset decode params: 0x%2.2x\n", res_reg[1]); - return -EIO; - } - - has_cd_task = NULL; - sony_inuse = 0; - wake_up_interruptible(&sony_wait); - - return(retval); -} - -static int -do_sony_cd_cmd_chk(const char *name, - unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, - unsigned int *result_size) -{ - do_sony_cd_cmd(cmd, params, num_params, result_buffer, result_size); - if ((*result_size < 2) || ((result_buffer[0] & 0xf0) == 0x20)) - { - printk("Sony CDROM error 0x%2.2x (CDROM%s)\n", result_buffer[1], name); - return -EIO; - } - return 0; -} - -/* - * The big ugly ioctl handler. - */ -static int scd_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - unsigned char res_reg[12]; - unsigned int res_size; - unsigned char params[7]; - int i; - - - if (!inode) - { - return -EINVAL; - } - - switch (cmd) - { - case CDROMSTART: /* Spin up the drive */ - return do_sony_cd_cmd_chk("START",SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - return 0; - break; - - case CDROMSTOP: /* Spin down the drive */ - do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); - - /* - * Spin the drive down, ignoring the error if the disk was - * already not spinning. - */ - sony_audio_status = CDROM_AUDIO_NO_STATUS; - return do_sony_cd_cmd_chk("STOP",SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); - - case CDROMPAUSE: /* Pause the drive */ - if(do_sony_cd_cmd_chk("PAUSE", SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size)) - return -EIO; - /* Get the current position and save it for resuming */ - if (read_subcode() < 0) - { - return -EIO; - } - cur_pos_msf[0] = last_sony_subcode.abs_msf[0]; - cur_pos_msf[1] = last_sony_subcode.abs_msf[1]; - cur_pos_msf[2] = last_sony_subcode.abs_msf[2]; - sony_audio_status = CDROM_AUDIO_PAUSED; - return 0; - break; - - case CDROMRESUME: /* Start the drive after being paused */ - if (sony_audio_status != CDROM_AUDIO_PAUSED) - { - return -EINVAL; - } - - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - - /* Start the drive at the saved position. */ - params[1] = cur_pos_msf[0]; - params[2] = cur_pos_msf[1]; - params[3] = cur_pos_msf[2]; - params[4] = final_pos_msf[0]; - params[5] = final_pos_msf[1]; - params[6] = final_pos_msf[2]; - params[0] = 0x03; - if(do_sony_cd_cmd_chk("RESUME",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0) - return -EIO; - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - - case CDROMPLAYMSF: /* Play starting at the given MSF address. */ - i=verify_area(VERIFY_READ, (char *) arg, 6); - if(i) - return i; - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - memcpy_fromfs(&(params[1]), (void *) arg, 6); - - /* The parameters are given in int, must be converted */ - for (i=1; i<7; i++) - { - params[i] = int_to_bcd(params[i]); - } - params[0] = 0x03; - if(do_sony_cd_cmd_chk("PLAYMSF",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0) - return -EIO; - - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = params[4]; - final_pos_msf[1] = params[5]; - final_pos_msf[2] = params[6]; - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - - case CDROMREADTOCHDR: /* Read the table of contents header */ - { - struct cdrom_tochdr *hdr; - struct cdrom_tochdr loc_hdr; - - sony_get_toc(); - if (!sony_toc_read) - { - return -EIO; - } - - hdr = (struct cdrom_tochdr *) arg; - i=verify_area(VERIFY_WRITE, hdr, sizeof(*hdr)); - if(i<0) - return i; - loc_hdr.cdth_trk0 = bcd_to_int(sony_toc.first_track_num); - loc_hdr.cdth_trk1 = bcd_to_int(sony_toc.last_track_num); - memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr)); - } - return 0; - - case CDROMREADTOCENTRY: /* Read a given table of contents entry */ - { - struct cdrom_tocentry *entry; - struct cdrom_tocentry loc_entry; - int track_idx; - unsigned char *msf_val = NULL; - - sony_get_toc(); - if (!sony_toc_read) - { - return -EIO; - } - - entry = (struct cdrom_tocentry *) arg; - i=verify_area(VERIFY_READ, entry, sizeof(*entry)); - if(i<0) - return i; - i=verify_area(VERIFY_WRITE, entry, sizeof(*entry)); - if(i<0) - return i; - - memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry)); - - /* Lead out is handled separately since it is special. */ - if (loc_entry.cdte_track == CDROM_LEADOUT) - { - loc_entry.cdte_adr = sony_toc.address2; - loc_entry.cdte_ctrl = sony_toc.control2; - msf_val = sony_toc.lead_out_start_msf; - } - else - { - track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); - if (track_idx < 0) - { - return -EINVAL; - } - - loc_entry.cdte_adr = sony_toc.tracks[track_idx].address; - loc_entry.cdte_ctrl = sony_toc.tracks[track_idx].control; - msf_val = sony_toc.tracks[track_idx].track_start_msf; - } - - /* Logical buffer address or MSF format requested? */ - if (loc_entry.cdte_format == CDROM_LBA) - { - loc_entry.cdte_addr.lba = msf_to_log(msf_val); - } - else if (loc_entry.cdte_format == CDROM_MSF) - { - loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); - loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1)); - loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2)); - } - memcpy_tofs(entry, &loc_entry, sizeof(*entry)); - } - return 0; - break; - - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - { - struct cdrom_ti ti; - int track_idx; - - sony_get_toc(); - if (!sony_toc_read) - { - return -EIO; - } - - i=verify_area(VERIFY_READ, (char *) arg, sizeof(ti)); - if(i<0) - return i; - - memcpy_fromfs(&ti, (char *) arg, sizeof(ti)); - if ( (ti.cdti_trk0 < sony_toc.first_track_num) - || (ti.cdti_trk0 > sony_toc.last_track_num) - || (ti.cdti_trk1 < ti.cdti_trk0)) - { - return -EINVAL; - } - - track_idx = find_track(int_to_bcd(ti.cdti_trk0)); - if (track_idx < 0) - { - return -EINVAL; - } - params[1] = sony_toc.tracks[track_idx].track_start_msf[0]; - params[2] = sony_toc.tracks[track_idx].track_start_msf[1]; - params[3] = sony_toc.tracks[track_idx].track_start_msf[2]; - - /* - * If we want to stop after the last track, use the lead-out - * MSF to do that. - */ - if (ti.cdti_trk1 >= bcd_to_int(sony_toc.last_track_num)) - { - log_to_msf(msf_to_log(sony_toc.lead_out_start_msf)-1, - &(params[4])); - } - else - { - track_idx = find_track(int_to_bcd(ti.cdti_trk1+1)); - if (track_idx < 0) - { - return -EINVAL; - } - log_to_msf(msf_to_log(sony_toc.tracks[track_idx].track_start_msf)-1, - &(params[4])); - } - params[0] = 0x03; - - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - - do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size); - - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) - { - printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1], - params[2], params[3], params[4], params[5], params[6]); - printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]); - return -EIO; - } - - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = params[4]; - final_pos_msf[1] = params[5]; - final_pos_msf[2] = params[6]; - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - } - - case CDROMSUBCHNL: /* Get subchannel info */ - return sony_get_subchnl_info(arg); - - case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ - { - struct cdrom_volctrl volctrl; - - i=verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl)); - if(i<0) - return i; - - memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl)); - params[0] = SONY_SD_AUDIO_VOLUME; - params[1] = volctrl.channel0; - params[2] = volctrl.channel1; - return do_sony_cd_cmd_chk("VOLCTRL",SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size); - } - case CDROMEJECT: /* Eject the drive */ - do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); - - sony_audio_status = CDROM_AUDIO_INVALID; - return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size); - - case CDROMREADAUDIO: /* Read 2352 byte audio tracks and 2340 byte - raw data tracks. */ - { - struct cdrom_read_audio ra; - - - sony_get_toc(); - if (!sony_toc_read) - { - return -EIO; - } - - i=verify_area(VERIFY_READ, (char *) arg, sizeof(ra)); - if(i<0) - return i; - memcpy_fromfs(&ra, (char *) arg, sizeof(ra)); - - i=verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes); - if(i<0) - return i; - - if (ra.addr_format == CDROM_LBA) - { - if ( (ra.addr.lba >= sony_toc.lead_out_start_lba) - || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba)) - { - return -EINVAL; - } - } - else if (ra.addr_format == CDROM_MSF) - { - if ( (ra.addr.msf.minute >= 75) - || (ra.addr.msf.second >= 60) - || (ra.addr.msf.frame >= 75)) - { - return -EINVAL; - } - - ra.addr.lba = ( (ra.addr.msf.minute * 4500) - + (ra.addr.msf.second * 75) - + ra.addr.msf.frame); - if ( (ra.addr.lba >= sony_toc.lead_out_start_lba) - || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba)) - { - return -EINVAL; - } - - /* I know, this can go negative on an unsigned. However, - the first thing done to the data is to add this value, - so this should compensate and allow direct msf access. */ - ra.addr.lba -= LOG_START_OFFSET; - } - else - { - return -EINVAL; - } - - return(read_audio(&ra, inode)); - } - return 0; - break; - - default: - return -EINVAL; - } -} - - -/* - * Open the drive for operations. Spin the drive up and read the table of - * contents if these have not already been done. - */ -static int -scd_open(struct inode *inode, - struct file *filp) -{ - unsigned char res_reg[12]; - unsigned int res_size; - int num_spin_ups; - unsigned char params[2]; - - - if ((filp) && filp->f_mode & 2) - return -EROFS; - - if (!sony_spun_up) - { - num_spin_ups = 0; - -respinup_on_open: - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - - /* The drive sometimes returns error 0. I don't know why, but ignore - it. It seems to mean the drive has already done the operation. */ - if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) - { - printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]); - return -EIO; - } - - do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); - - /* The drive sometimes returns error 0. I don't know why, but ignore - it. It seems to mean the drive has already done the operation. */ - if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) - { - /* If the drive is already playing, it's ok. */ - if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0)) - { - goto drive_spinning; - } - - /* If the drive says it is not spun up (even though we just did it!) - then retry the operation at least a few times. */ - if ( (res_reg[1] == SONY_NOT_SPIN_ERR) - && (num_spin_ups < MAX_CDU31A_RETRIES)) - { - num_spin_ups++; - goto respinup_on_open; - } - - printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]); - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); - - return -EIO; - } - - sony_get_toc(); - if (!sony_toc_read) - { - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); - return -EIO; - } - - /* For XA on the CDU31A only, we have to do special reads. - The CDU33A handles XA automagically. */ - if ( (sony_toc.disk_type == SONY_XA_DISK_TYPE) - && (!is_double_speed)) - { - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x07; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) - { - printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]); - } - sony_xa_mode = 1; - } - /* A non-XA disk. Set the parms back if necessary. */ - else if (sony_xa_mode) - { - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x0f; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) - { - printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]); - } - sony_xa_mode = 0; - } - - sony_spun_up = 1; - } - -drive_spinning: - - /* If filp is not NULL (standard open), try a disk change. */ - if (filp) - { - check_disk_change(inode->i_rdev); - } - - sony_usage++; - - return 0; -} - - -/* - * Close the drive. Spin it down if no task is using it. The spin - * down will fail if playing audio, so audio play is OK. - */ -static void -scd_release(struct inode *inode, - struct file *filp) -{ - unsigned char res_reg[12]; - unsigned int res_size; - - - if (sony_usage > 0) - { - sony_usage--; - } - if (sony_usage == 0) - { - sync_dev(inode->i_rdev); - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); - - sony_spun_up = 0; - } -} - - -static struct file_operations scd_fops = { - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* select */ - scd_ioctl, /* ioctl */ - NULL, /* mmap */ - scd_open, /* open */ - scd_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - scd_disk_change, /* media_change */ - NULL /* revalidate */ -}; - - -/* The different types of disc loading mechanisms supported */ -static const char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" }; - -static void -get_drive_configuration(unsigned short base_io, - unsigned char res_reg[], - unsigned int *res_size) -{ - int retry_count; - - - /* Set the base address */ - cdu31a_port = base_io; - - /* Set up all the register locations */ - sony_cd_cmd_reg = cdu31a_port + SONY_CMD_REG_OFFSET; - sony_cd_param_reg = cdu31a_port + SONY_PARAM_REG_OFFSET; - sony_cd_write_reg = cdu31a_port + SONY_WRITE_REG_OFFSET; - sony_cd_control_reg = cdu31a_port + SONY_CONTROL_REG_OFFSET; - sony_cd_status_reg = cdu31a_port + SONY_STATUS_REG_OFFSET; - sony_cd_result_reg = cdu31a_port + SONY_RESULT_REG_OFFSET; - sony_cd_read_reg = cdu31a_port + SONY_READ_REG_OFFSET; - sony_cd_fifost_reg = cdu31a_port + SONY_FIFOST_REG_OFFSET; - - /* - * Check to see if anything exists at the status register location. - * I don't know if this is a good way to check, but it seems to work - * ok for me. - */ - if (read_status_register() != 0xff) - { - /* - * Reset the drive and wait for attention from it (to say it's reset). - * If you don't wait, the next operation will probably fail. - */ - reset_drive(); - retry_count = jiffies + SONY_RESET_TIMEOUT; - while ((retry_count > jiffies) && (!is_attention())) - { - sony_sleep(); - } - -#if 0 - /* If attention is never seen probably not a CDU31a present */ - if (!is_attention()) - { - res_reg[0] = 0x20; - return; - } -#endif - - /* - * Get the drive configuration. - */ - do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD, - NULL, - 0, - (unsigned char *) res_reg, - res_size); - return; - } - - /* Return an error */ - res_reg[0] = 0x20; -} - -/* - * Set up base I/O and interrupts, called from main.c. - */ -void -cdu31a_setup(char *strings, - int *ints) -{ - if (ints[0] > 0) - { - cdu31a_port = ints[1]; - } - if (ints[0] > 1) - { - cdu31a_irq = ints[2]; - } - if ((strings != NULL) && (*strings != '\0')) - { - if (strcmp(strings, "PAS") == 0) - { - sony_pas_init = 1; - } - else - { - printk("CDU31A: Unknown interface type: %s\n", strings); - } - } -} - -static int cdu31a_block_size; - -/* - * Initialize the driver. - */ -#ifdef MODULE -#define cdu31a_init init_module -#endif - -int -cdu31a_init(void) -{ - struct s_sony_drive_config drive_config; - unsigned int res_size; - int i; - int drive_found; - int tmp_irq; - - - /* - * According to Alex Freed (freed@europa.orion.adobe.com), this is - * required for the Fusion CD-16 package. If the sound driver is - * loaded, it should work fine, but just in case... - * - * The following turn on the CD-ROM interface for a Fusion CD-16. - */ - if (sony_pas_init) - { - outb(0xbc, 0x9a01); - outb(0xe2, 0x9a01); - } - - drive_found = 0; - - /* Setting the base I/O address to 0xffff will disable it. */ - if (cdu31a_port == 0xffff) - { - } - else if (cdu31a_port != 0) - { - tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */ - cdu31a_irq = 0; - - get_drive_configuration(cdu31a_port, - drive_config.exec_status, - &res_size); - if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00)) - { - drive_found = 1; - } - - cdu31a_irq = tmp_irq; - } - else - { - cdu31a_irq = 0; - i = 0; - while ( (cdu31a_addresses[i].base != 0) - && (!drive_found)) - { - if (check_region(cdu31a_addresses[i].base, 4)) { - i++; - continue; - } - get_drive_configuration(cdu31a_addresses[i].base, - drive_config.exec_status, - &res_size); - if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00)) - { - drive_found = 1; - cdu31a_irq = cdu31a_addresses[i].int_num; - } - else - { - i++; - } - } - } - - if (drive_found) - { - request_region(cdu31a_port, 4,"cdu31a"); - - if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops)) - { - printk("Unable to get major %d for CDU-31a\n", MAJOR_NR); - return -EIO; - } - - if (SONY_HWC_DOUBLE_SPEED(drive_config)) - { - is_double_speed = 1; - } - - tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */ - cdu31a_irq = 0; - - set_drive_params(); - - cdu31a_irq = tmp_irq; - - if (cdu31a_irq > 0) - { - if (request_irq(cdu31a_irq, cdu31a_interrupt, SA_INTERRUPT, "cdu31a")) - { - printk("Unable to grab IRQ%d for the CDU31A driver\n", cdu31a_irq); - cdu31a_irq = 0; - } - } - - printk("Sony I/F CDROM : %8.8s %16.16s %8.8s\n", - drive_config.vendor_id, - drive_config.product_id, - drive_config.product_rev_level); - printk(" Capabilities: %s", - load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]); - if (SONY_HWC_AUDIO_PLAYBACK(drive_config)) - { - printk(", audio"); - } - if (SONY_HWC_EJECT(drive_config)) - { - printk(", eject"); - } - if (SONY_HWC_LED_SUPPORT(drive_config)) - { - printk(", LED"); - } - if (SONY_HWC_ELECTRIC_VOLUME(drive_config)) - { - printk(", elec. Vol"); - } - if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config)) - { - printk(", sep. Vol"); - } - if (is_double_speed) - { - printk(", double speed"); - } - if (cdu31a_irq > 0) - { - printk(", irq %d", cdu31a_irq); - } - printk("\n"); - - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; - read_ahead[MAJOR_NR] = CDU31A_READAHEAD; - cdu31a_block_size = 1024; /* 1kB default block size */ - /* use 'mount -o block=2048' */ - blksize_size[MAJOR_NR] = &cdu31a_block_size; - - cdu31a_abort_timer.next = NULL; - cdu31a_abort_timer.prev = NULL; - cdu31a_abort_timer.function = handle_abort_timeout; - } - - - disk_changed = 1; - - if (drive_found) - { - return(0); - } - else - { - return -EIO; - } -} - -#ifdef MODULE -void -cleanup_module(void) -{ - if (sony_usage != 0) - { - printk("cdu31a module in use - can't remove it.\n"); - return; - } - - if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) - { - printk("Can't unregister cdu31a\n"); - return; - } - release_region(cdu31a_port,4); - printk("cdu31a module released.\n"); -} -#endif MODULE diff -u --recursive --new-file v1.3.35/linux/drivers/block/cm206.c linux/drivers/block/cm206.c --- v1.3.35/linux/drivers/block/cm206.c Tue Oct 10 18:46:31 1995 +++ linux/drivers/block/cm206.c Thu Jan 1 02:00:00 1970 @@ -1,1249 +0,0 @@ -/* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card. - Copyright (c) 1995 David van Leeuwen. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -History: - Started 25 jan 1994. Waiting for documentation... - 22 feb 1995: 0.1a first reasonably safe polling driver. - Two major bugs, one in read_sector and one in - do_cm206_request, happened to cancel! - 25 feb 1995: 0.2a first reasonable interrupt driven version of above. - uart writes are still done in polling mode. - 25 feb 1995: 0.21a writes also in interrupt mode, still some - small bugs to be found... Larger buffer. - 2 mrt 1995: 0.22 Bug found (cd-> nowhere, interrupt was called in - initialization), read_ahead of 16. Timeouts implemented. - unclear if they do something... - 7 mrt 1995: 0.23 Start of background read-ahead. - 18 mrt 1995: 0.24 Working background read-ahead. (still problems) - 26 mrt 1995: 0.25 Multi-session ioctl added (kernel v1.2). - Statistics implemented, though separate stats206.h. - Accessible trough ioctl 0x1000 (just a number). - Hard to choose between v1.2 development and 1.1.75. - Bottom-half doesn't work with 1.2... - 0.25a: fixed... typo. Still problems... - 1 apr 1995: 0.26 Module support added. Most bugs found. Use kernel 1.2.n. - 5 apr 1995: 0.27 Auto-probe for the adapter card base address. - Auto-probe for the adaptor card irq line. - 7 apr 1995: 0.28 Added lilo setup support for base address and irq. - Use major number 32 (not in this source), officially - assigned to this driver. - 9 apr 1995: 0.29 Added very limited audio support. Toc_header, stop, pause, - resume, eject. Play_track ignores track info, because we can't - read a table-of-contents entry. Toc_entry is implemented - as a `placebo' function: always returns start of disc. - 3 may 1995: 0.30 Audio support completed. The get_toc_entry function - is implemented as a binary search. - 15 may 1995: 0.31 More work on audio stuff. Workman is not easy to - satisfy; changed binary search into linear search. - Auto-probe for base address somewhat relaxed. - 1 jun 1995: 0.32 Removed probe_irq_on/off for module version. - 10 jun 1995: 0.33 Workman still behaves funny, but you should be - able to eject and substitute another disc. - - An adaption of 0.33 is included in linux-1.3.7 by Eberhard Moenkeberg - - 18 jul 1996: 0.34 Patch by Heiko Eissfeldt included, mainly considering - verify_area's in the ioctls. Some bugs introduced by - EM considering the base port and irq fixed. - * - * Parts of the code are based upon lmscd.c written by Kai Petzke, - * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin - * Harriss, but any off-the-shelf dynamic programming algorithm won't - * be able to find them. - * - * The cm206 drive interface and the cm260 adapter card seem to be - * sufficiently different from their cm205/cm250 counterparts - * in order to write a complete new driver. - * - * I call all routines connected to the Linux kernel something - * with `cm206' in it, as this stuff is too series-dependent. - * - * Currently, my limited knowledge is based on: - * - The Linux Kernel Hacker's guide, v. 0.5 , by Michael J. Johnson - * - Linux Kernel Programmierung, by Michael Beck and others - * - Philips/LMS cm206 and cm226 product specification - * - Philips/LMS cm260 product specification - * - * David van Leeuwen, david@tm.tno.nl. */ -#define VERSION "0.34" - -#ifdef MODULE /* OK, so some of this is stolen */ -#include -#include -#ifndef CONFIG_MODVERSIONS -char kernel_version[]=UTS_RELEASE; -#endif -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif MODULE - -#include /* These include what we really need */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MAJOR_NR CM206_CDROM_MAJOR -#include "blk.h" -#include - -/* 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 -#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; ibackground) 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; inr_sectors; i++) { - cd_sec_no = (CURRENT->sector+i)/4; /* 4 times 512 bytes */ - quarter = (CURRENT->sector+i) % 4; - dest = CURRENT->buffer + i*512; - /* is already in buffer memory? */ - if (cd->sector_first <= cd_sec_no && cd_sec_no < cd->sector_last) { - source = ((uch *) cd->sector) + 16 + - quarter*512 + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE; - memcpy(dest, source, 512); - } - else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) { - source = ((uch *) cd->sector)+16+quarter*512; - memcpy(dest, source, 512); - } - else { - error=1; - } - } - end_request(!error); - } -} - -int get_multi_session_info(struct cdrom_multisession * mssp) -{ - if (!FIRST_TRACK) get_disc_status(); - if (mssp) { - if (DISC_STATUS & cds_multi_session) { /* multi-session */ - if (mssp->addr_format == CDROM_LBA) - mssp->addr.lba = fsm2lba(&cd->disc_status[3]); - else { - mssp->addr.msf.frame = cd->disc_status[3]; - mssp->addr.msf.second = cd->disc_status[4]; - mssp->addr.msf.minute = cd->disc_status[5]; - } - mssp->xa_flag = 1; - } else { - mssp->xa_flag = 0; - } - return 1; - } - return 0; -} - -/* Audio support. I've tried very hard, but the cm206 drive doesn't - seem to have a get_toc (table-of-contents) function, while i'm - pretty sure it must read the toc upon disc insertion. Therefore - this function has been implemented through a binary search - strategy. All track starts that happen to be found are stored in - cd->toc[], for future use. - - I've spent a whole day on a bug that only shows under Workman--- - I don't get it. Tried everything, nothing works. If workman asks - for track# 0xaa, it'll get the wrong time back. Any other program - receives the correct value. I'm stymied. -*/ - -/* seek seeks to address lba. It does wait to arrive there. */ -void seek(int lba) -{ - int i; - uch seek_command[4]={c_seek, }; - - fsm(lba, &seek_command[1]); - for (i=0; i<4; i++) type_0_command(seek_command[i], 0); - cd->dsb = wait_dsb(); -} - -uch bcdbin(unsigned char bcd) /* stolen from mcd.c! */ -{ - return (bcd >> 4)*10 + (bcd & 0xf); -} - -inline uch normalize_track(uch track) -{ - if (track<1) return 1; - if (track>LAST_TRACK) return LAST_TRACK+1; - return track; -} - -/* This function does a binary search for track start. It records all - * tracks seen in the process. Input $track$ must be between 1 and - * #-of-tracks+1 */ -int get_toc_lba(uch track) -{ - int max=74*60*75-150, min=0; - int i, lba, l, old_lba=0; - uch * q = cd->q; - uch ct; /* current track */ - int binary=0; - const skip = 3*60*75; - - for (i=track; i>0; i--) if (cd->toc[i].track) { - min = fsm2lba(cd->toc[i].fsm); - break; - } - lba = min + skip; /* 3 minutes */ - do { - seek(lba); - type_1_command(c_read_current_q, 10, q); - ct = normalize_track(q[1]); - if (!cd->toc[ct].track) { - l = q[9]-bcdbin(q[5]) + 75*(q[8]-bcdbin(q[4])-2 + - 60*(q[7]-bcdbin(q[3]))); - cd->toc[ct].track=q[1]; /* lead out still 0xaa */ - fsm(l, cd->toc[ct].fsm); - cd->toc[ct].q0 = q[0]; /* contains adr and ctrl info */ -/* - if (ct==LAST_TRACK+1) - printk("Leadout %x %x %x %x %d %d %d \n", q[1], q[3], q[4], q[5], - q[7], q[8], q[9]); -*/ - if (ct==track) return l; - } - old_lba=lba; - if (binary) { - if (ct < track) min = lba; else max = lba; - lba = (min+max)/2; - } else { - if(ct < track) lba += skip; - else { - binary=1; - max = lba; min = lba - skip; - lba = (min+max)/2; - } - } - } while (lba!=old_lba); - return lba; -} - -void update_toc_entry(uch track) -{ - track = normalize_track(track); - if (!cd->toc[track].track) get_toc_lba(track); -} - -int read_toc_header(struct cdrom_tochdr * hp) -{ - if (!FIRST_TRACK) get_disc_status(); - if (hp && DISC_STATUS & cds_all_audio) { /* all audio */ - int i; - hp->cdth_trk0 = FIRST_TRACK; - hp->cdth_trk1 = LAST_TRACK; - cd->toc[1].track=1; /* fill in first track position */ - for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i]; - update_toc_entry(LAST_TRACK+1); /* find most entries */ - return 1; - } - return 0; -} - -void play_from_to_msf(struct cdrom_msf* msfp) -{ - uch play_command[] = {c_play, - msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0, - msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2, 2}; - int i; - for (i=0; i<9; i++) type_0_command(play_command[i], 0); - for (i=0; i<3; i++) - PLAY_TO.fsm[i] = play_command[i+4]; - PLAY_TO.track = 0; /* say no track end */ - cd->dsb = wait_dsb(); -} - -void play_from_to_track(int from, int to) -{ - uch play_command[8] = {c_play, }; - int i; - - if (from==0) { /* continue paused play */ - for (i=0; i<3; i++) { - play_command[i+1] = cd->audio_status[i+2]; - play_command[i+4] = PLAY_TO.fsm[i]; - } - } else { - update_toc_entry(from); update_toc_entry(to+1); - for (i=0; i<3; i++) { - play_command[i+1] = cd->toc[from].fsm[i]; - PLAY_TO.fsm[i] = play_command[i+4] = cd->toc[to+1].fsm[i]; - } - PLAY_TO.track = to; - } - for (i=0; i<7; i++) type_0_command(play_command[i],0); - for (i=0; i<2; i++) type_0_command(0x2, 0); /* volume */ - cd->dsb = wait_dsb(); -} - -int get_current_q(struct cdrom_subchnl * qp) -{ - int i; - uch * q = cd->q; - if (type_1_command(c_read_current_q, 10, q)) return 0; -/* q[0] = bcdbin(q[0]); Don't think so! */ - for (i=2; i<6; i++) q[i]=bcdbin(q[i]); - qp->cdsc_adr = q[0] & 0xf; qp->cdsc_ctrl = q[0] >> 4; /* from mcd.c */ - qp->cdsc_trk = q[1]; qp->cdsc_ind = q[2]; - if (qp->cdsc_format == CDROM_MSF) { - qp->cdsc_reladdr.msf.minute = q[3]; - qp->cdsc_reladdr.msf.second = q[4]; - qp->cdsc_reladdr.msf.frame = q[5]; - qp->cdsc_absaddr.msf.minute = q[7]; - qp->cdsc_absaddr.msf.second = q[8]; - qp->cdsc_absaddr.msf.frame = q[9]; - } else { - qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]); - qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]); - } - get_drive_status(); - if (cd->dsb & dsb_play_in_progress) - qp->cdsc_audiostatus = CDROM_AUDIO_PLAY ; - else if (PAUSED) - qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED; - else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; - return 1; -} - -void get_toc_entry(struct cdrom_tocentry * ep) -{ - uch track = normalize_track(ep->cdte_track); - update_toc_entry(track); - if (ep->cdte_format == CDROM_MSF) { - ep->cdte_addr.msf.frame = cd->toc[track].fsm[0]; - ep->cdte_addr.msf.second = cd->toc[track].fsm[1]; - ep->cdte_addr.msf.minute = cd->toc[track].fsm[2]; - } - else ep->cdte_addr.lba = fsm2lba(cd->toc[track].fsm); - ep->cdte_adr = cd->toc[track].q0 & 0xf; - ep->cdte_ctrl = cd->toc[track].q0 >> 4; - ep->cdte_datamode=0; -} - -/* Ioctl. I have made the statistics accessible through an ioctl - call. The constant is defined in cm206.h, it shouldn't clash with - the standard Linux ioctls. Multisession info is gathered at - run-time, this may turn out to be slow. */ - -static int cm206_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { -#ifdef STATISTICS - case CM206CTL_GET_STAT: - if (arg >= NR_STATS) return -EINVAL; - else return cd->stats[arg]; - case CM206CTL_GET_LAST_STAT: - if (arg >= NR_STATS) return -EINVAL; - else return cd->last_stat[arg]; -#endif - case CDROMMULTISESSION: { - struct cdrom_multisession ms_info; - int st; - stats(ioctl_multisession); - - st=verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct cdrom_multisession)); - if (st) return (st); - memcpy_fromfs(&ms_info, (struct cdrom_multisession *) arg, - sizeof(struct cdrom_multisession)); - get_multi_session_info(&ms_info); - memcpy_tofs((struct cdrom_multisession *) arg, &ms_info, - sizeof(struct cdrom_multisession)); - return 0; - } - case CDROMRESET: /* If needed, it's probably too late anyway */ - stop_read(); - reset_cm260(); - outw(dc_normal | dc_break | READ_AHEAD, r_data_control); - udelay(1000); /* 750 musec minimum */ - outw(dc_normal | READ_AHEAD, r_data_control); - cd->sector_last = -1; /* flag no data buffered */ - cd->adapter_last = -1; - return 0; - } - - get_drive_status(); - if (cd->dsb & (dsb_drive_not_ready | dsb_tray_not_closed) ) - return -EAGAIN; - - switch (cmd) { - case CDROMREADTOCHDR: { - struct cdrom_tochdr header; - int st; - - st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(header)); - if (st) return (st); - if (read_toc_header(&header)) { - memcpy_tofs((struct cdrom_tochdr *) arg, &header, sizeof(header)); - return 0; - } - else return -ENODATA; - } - case CDROMREADTOCENTRY: { - struct cdrom_tocentry entry; - int st; - - st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry)); - if (st) return (st); - memcpy_fromfs(&entry, (struct cdrom_tocentry *) arg, sizeof entry); - get_toc_entry(&entry); - memcpy_tofs((struct cdrom_tocentry *) arg, &entry, sizeof entry); - return 0; - } - case CDROMPLAYMSF: { - struct cdrom_msf msf; - int st; - - st=verify_area(VERIFY_READ, (void *) arg, sizeof(msf)); - if (st) return (st); - memcpy_fromfs(&msf, (struct cdrom_mdf *) arg, sizeof msf); - play_from_to_msf(&msf); - return 0; - } - case CDROMPLAYTRKIND: { - struct cdrom_ti track_index; - int st; - - st=verify_area(VERIFY_READ, (void *) arg, sizeof(track_index)); - if (st) return (st); - memcpy_fromfs(&track_index, (struct cdrom_ti *) arg, sizeof(track_index)); - play_from_to_track(track_index.cdti_trk0, track_index.cdti_trk1); - return 0; - } - case CDROMSTOP: - PAUSED=0; - if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1); - return 0; - case CDROMPAUSE: - if (cd->dsb & dsb_play_in_progress) { - type_0_command(c_stop, 1); - type_1_command(c_audio_status, 5, cd->audio_status); - PAUSED=1; /* say we're paused */ - } - return 0; - case CDROMRESUME: - if (PAUSED) play_from_to_track(0,0); - PAUSED=0; - return 0; - case CDROMEJECT: - PAUSED=0; - if (cd->openfiles == 1) { /* Must do an open before an eject! */ - type_0_command(c_open_tray,1); - memset(cd->toc, 0, sizeof(cd->toc)); - memset(cd->disc_status, 0, sizeof(cd->disc_status)); - return 0; - } - else return -EBUSY; - case CDROMSTART: - case CDROMVOLCTRL: - return 0; - case CDROMSUBCHNL: { - struct cdrom_subchnl q; - int st; - - st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(q)); - if (st) return (st); - memcpy_fromfs(&q, (struct cdrom_subchnl *) arg, sizeof q); - if (get_current_q(&q)) { - memcpy_tofs((struct cdrom_subchnl *) arg, &q, sizeof q); - return 0; - } - else return -cmd; - } - case CDROM_GET_UPC: { - uch upc[10]; - int st; - - st=verify_area(VERIFY_WRITE, (void *) arg, 8); - if (st) return (st); - if (type_1_command(c_read_upc, 10, upc)) return -EIO; - memcpy_tofs((uch *) arg, &upc[1], 8); - return 0; - } - default: - debug(("Unknown ioctl call 0x%x\n", cmd)); - return -EINVAL; - } -} - -/* from lmscd.c */ -static struct file_operations cm206_fops = { - NULL, /* lseek */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir */ - NULL, /* select */ - cm206_ioctl, /* ioctl */ - NULL, /* mmap */ - cm206_open, /* open */ - cm206_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* media_change */ - NULL /* revalidate */ -}; - -/* This routine gets called during init if thing go wrong, can be used - * in cleanup_module as well. */ -void cleanup(int level) -{ - switch (level) { - case 4: - if (unregister_blkdev(MAJOR_NR, "cm206")) { - printk("Can't unregister cm206\n"); - return; - } - case 3: - free_irq(cm206_irq); - case 2: - case 1: - kfree(cd); - release_region(cm206_base, 16); - default: - } -} - -/* This function probes for the adapter card. It returns the base - address if it has found the adapter card. One can specify a base - port to probe specifically, or 0 which means span all possible - bases. - - Linus says it is too dangerous to use writes for probing, so we - stick with pure reads for a while. Hope that 8 possible ranges, - check_region, 15 bits of one port and 6 of another make things - likely enough to accept the region on the first hit... - */ -int probe_base_port(int base) -{ - int b=0x300, e=0x370; /* this is the range of start addresses */ - volatile int fool; -#if 0 - const pattern1=0x65, pattern2=0x1a; -#endif - - if (base) b=e=base; - for (base=b; base<=e; base += 0x10) { - if (check_region(base, 0x10)) continue; - fool = inw(base+2); /* empty possibly uart_receive_buffer */ - if((inw(base+6) & 0xffef) != 0x0001 || /* line_status */ - (inw(base) & 0xad00) != 0) /* data status */ - continue; -#if 0 /* writes... dangerous... */ - outw(dc_normal | pattern1, base+8); - if ((inw(base) & 0x7f) != pattern1) continue; - outw(dc_normal | pattern2, base+8); - if ((inw(base) & 0x7f) != pattern2) continue; - outw(dc_normal | READ_AHEAD, base+8); -#endif - return(base); - } - return 0; -} - -#if !defined(MODULE) || defined(AUTO_PROBE_MODULE) -/* Probe for irq# nr. If nr==0, probe for all possible irq's. */ -int probe_irq(int nr) { - int irqs, irq; - outw(dc_normal | READ_AHEAD, r_data_control); /* disable irq-generation */ - sti(); - irqs = probe_irq_on(); - reset_cm260(); /* causes interrupt */ - udelay(10); /* wait for it */ - irq = probe_irq_off(irqs); - outw(dc_normal | READ_AHEAD, r_data_control); /* services interrupt */ - if (nr && irq!=nr && irq>0) return 0; /* wrong interrupt happened */ - else return irq; -} -#endif - -#ifdef MODULE - -static int cm206[2] = {0,0}; /* for compatible `insmod' parameter passing */ -void parse_options(void) -{ - int i; - for (i=0; i<2; i++) { - if (0x300 <= cm206[i] && i<= 0x370 && cm206[i] % 0x10 == 0) { - cm206_base = cm206[i]; - auto_probe=0; - } - else if (3 <= cm206[i] && cm206[i] <= 15) { - cm206_irq = cm206[i]; - auto_probe=0; - } - } -} - -#define cm206_init init_module - -#endif MODULE - - -int cm206_init(void) -{ - uch e=0; - long int size=sizeof(struct cm206_struct); - - printk("cm206: v" VERSION); -#if defined(MODULE) - parse_options(); -#if !defined(AUTO_PROBE_MODULE) - auto_probe=0; -#endif -#endif - cm206_base = probe_base_port(auto_probe ? 0 : cm206_base); - if (!cm206_base) { - printk(" can't find adapter!\n"); - return -EIO; - } - printk(" adapter at 0x%x", cm206_base); - request_region(cm206_base, 16, "cm206"); - cd = (struct cm206_struct *) kmalloc(size, GFP_KERNEL); - if (!cd) return -EIO; - /* Now we have found the adaptor card, try to reset it. As we have - * found out earlier, this process generates an interrupt as well, - * so we might just exploit that fact for irq probing! */ -#if !defined(MODULE) || defined(AUTO_PROBE_MODULE) - cm206_irq = probe_irq(auto_probe ? 0 : cm206_irq); - if (cm206_irq<=0) { - printk("can't find IRQ!\n"); - cleanup(1); - return -EIO; - } - else printk(" IRQ %d found\n", cm206_irq); -#else - reset_cm260(); - printk(" using IRQ %d\n", cm206_irq); -#endif - if (send_receive_polled(c_drive_configuration) != c_drive_configuration) - { - printk(" drive not there\n"); - cleanup(1); - return -EIO; - } - e = send_receive_polled(c_gimme); - printk("Firmware revision %d", e & dcf_revision_code); - if (e & dcf_transfer_rate) printk(" double"); - else printk(" single"); - printk(" speed drive"); - if (e & dcf_motorized_tray) printk(", motorized tray"); - if (request_irq(cm206_irq, cm206_interrupt, 0, "cm206")) { - printk("\nUnable to reserve IRQ---aborted\n"); - cleanup(2); - return -EIO; - } - printk(".\n"); - if (register_blkdev(MAJOR_NR, "cm206", &cm206_fops) != 0) { - printk("Cannot register for major %d!\n", MAJOR_NR); - cleanup(3); - return -EIO; - } - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; - read_ahead[MAJOR_NR] = 16; /* reads ahead what? */ - bh_base[CM206_BH].routine = cm206_bh; - enable_bh(CM206_BH); - - memset(cd, 0, sizeof(*cd)); /* give'm some reasonable value */ - cd->sector_last = -1; /* flag no data buffered */ - cd->adapter_last = -1; - cd->timer.function = cm206_timeout; - cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97; - printk("%d kB adapter memory available, " - " %ld bytes kernel memory used.\n", cd->max_sectors*2, size); - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - cleanup(4); - printk("cm206 removed\n"); -} - -#else MODULE - -/* This setup function accepts either `auto' or numbers in the range - * 3--11 (for irq) or 0x300--0x370 (for base port) or both. */ -void cm206_setup(char *s, int *p) -{ - int i; - if (!strcmp(s, "auto")) auto_probe=1; - for(i=1; i<=p[0]; i++) { - if (0x300 <= p[i] && i<= 0x370 && p[i] % 0x10 == 0) { - cm206_base = p[i]; - auto_probe = 0; - } - else if (3 <= p[i] && p[i] <= 15) { - cm206_irq = p[i]; - auto_probe = 0; - } - } -} -#endif MODULE diff -u --recursive --new-file v1.3.35/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v1.3.35/linux/drivers/block/floppy.c Fri Oct 13 14:44:32 1995 +++ linux/drivers/block/floppy.c Sat Oct 21 19:37:35 1995 @@ -81,12 +81,21 @@ * errors to allow safe writing by specialized programs. */ +/* + * 1995/8/26 -- Andreas Busse -- added Mips support. + */ + /* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks * by defining bit 1 of the "stretch" parameter to mean put sectors on the * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's * drives are "upside-down"). */ +/* + * 1995/18/10 -- Ralf Baechle -- Portability cleanup; move machine dependend + * features to asm/floppy.h. + */ + #define CONFIG_FLOPPY_SANITY #undef CONFIG_FLOPPY_SILENT_DCL_CLEAR @@ -113,8 +122,6 @@ int FLOPPY_IRQ=6; int FLOPPY_DMA=2; int ALLOWED_DRIVE_MASK = 0x33; -int FDC1 = 0x3f0; -int FDC2 = -1; #endif @@ -128,8 +135,6 @@ #define FLOPPY_IRQ 6 #define FLOPPY_DMA 2 -#define FDC1 0x3f0 -static int FDC2=-1; #endif #define MODULE_AWARE_DRIVER @@ -223,17 +228,19 @@ #include #include +#include #include #include #include #include #define MAJOR_NR FLOPPY_MAJOR -#include "blk.h" - +#include -/* Dma Memory related stuff */ +/* + * Dma Memory related stuff + */ /* Pure 2^n version of get_order */ static inline int __get_order (int size) { @@ -260,25 +267,9 @@ return __get_dma_pages(GFP_KERNEL,order); } -/* End dma memory related stuff */ - static unsigned int fake_change = 0; static int initialising=1; -/* - * Again, the CMOS information doesn't work on the alpha.. - */ -#ifdef __alpha__ -#define FLOPPY0_TYPE 6 -#define FLOPPY1_TYPE 0 -#else -#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15) -#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15) -#endif - -#define N_FDC 2 -#define N_DRIVE 8 - static inline int TYPE(kdev_t x) { return (MINOR(x)>>2) & 0x1f; } @@ -343,24 +334,9 @@ */ #define MAX_DISK_SIZE 2 /* 3984*/ - #define K_64 0x10000 /* 64KB */ /* - * The DMA channel used by the floppy controller cannot access data at - * addresses >= 16MB - * - * Went back to the 1MB limit, as some people had problems with the floppy - * driver otherwise. It doesn't matter much for performance anyway, as most - * floppy accesses go through the track buffer. - */ -#ifdef __alpha__ -# define CROSS_64KB(a,s) (0) -#else -# define CROSS_64KB(a,s) ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64) -#endif - -/* * globals used by 'result()' */ #define MAX_REPLIES 10 @@ -737,13 +713,13 @@ if (UDP->flags & FD_DEBUG){ DPRINT1("checking disk change line for drive %d\n",drive); DPRINT1("jiffies=%ld\n", jiffies); - DPRINT1("disk change line=%x\n",inb_p(FD_DIR)&0x80); + DPRINT1("disk change line=%x\n",fd_inb(FD_DIR)&0x80); DPRINT1("flags=%x\n",UDRS->flags); } #endif if (UDP->flags & FD_BROKEN_DCL) return UTESTF(FD_DISK_CHANGED); - if( (inb_p(FD_DIR) ^ UDP->flags) & 0x80){ + if( (fd_inb(FD_DIR) ^ UDP->flags) & 0x80){ USETF(FD_VERIFY); /* verify write protection */ if(UDRS->maxblock){ /* mark it changed */ @@ -794,7 +770,7 @@ disk_change(drive); } FDCS->dor = newdor; - outb_p(newdor, FD_DOR); + fd_outb(newdor, FD_DOR); unit = newdor & 0x3; if(!is_selected(olddor, unit) && is_selected(newdor,unit)){ @@ -813,8 +789,8 @@ { if (DP->select_delay) return; - outb_p(FDCS->dor & ~(0x10<dor, FD_DOR); + fd_outb(FDCS->dor & ~(0x10<dor, FD_DOR); DRS->select_date = jiffies; } @@ -845,7 +821,7 @@ set_dor(1-fdc, ~8, 0); if ( FDCS->rawcmd == 2 ) reset_fdc_info(1); - if( inb_p(FD_STATUS) != STATUS_READY ) + if ( fd_inb(FD_STATUS) != STATUS_READY ) FDCS->reset = 1; } @@ -1092,14 +1068,13 @@ } #endif cli(); - disable_dma(FLOPPY_DMA); - clear_dma_ff(FLOPPY_DMA); - set_dma_mode(FLOPPY_DMA, - (raw_cmd->flags & FD_RAW_READ)? - DMA_MODE_READ : DMA_MODE_WRITE); - set_dma_addr(FLOPPY_DMA, virt_to_bus(raw_cmd->kernel_data)); - set_dma_count(FLOPPY_DMA, raw_cmd->length); - enable_dma(FLOPPY_DMA); + fd_disable_dma(); + fd_clear_dma_ff(); + fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)? + DMA_MODE_READ : DMA_MODE_WRITE); + fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data)); + fd_set_dma_count(raw_cmd->length); + fd_enable_dma(); sti(); floppy_disable_hlt(); } @@ -1114,12 +1089,12 @@ if (FDCS->reset) return -1; for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) { - rstatus = inb_p(FD_STATUS); + rstatus = fd_inb(FD_STATUS); status = rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA); if (!(status & STATUS_READY)) continue; if (status == STATUS_READY){ - outb_p(byte,FD_DATA); + fd_outb(byte,FD_DATA); #ifdef CONFIG_FLOPPY_SANITY output_log[output_log_pos].data = byte; @@ -1147,7 +1122,7 @@ if (FDCS->reset) return -1; for (counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) { - status = inb_p(FD_STATUS)& + status = fd_inb(FD_STATUS)& (STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA); if (!(status & STATUS_READY)) continue; @@ -1165,7 +1140,7 @@ DPRINT("floppy_stat reply overrun\n"); break; } - reply_buffer[i++] = inb_p(FD_DATA); + reply_buffer[i++] = fd_inb(FD_DATA); } } FDCS->reset = 1; @@ -1321,7 +1296,7 @@ return 0; /* Set dtr */ - outb_p(raw_cmd->rate, FD_DCR); + fd_outb(raw_cmd->rate, FD_DCR); /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) * need a stabilization period of several milliseconds to be @@ -1751,11 +1726,11 @@ FDCS->reset = 0; reset_fdc_info(0); if ( FDCS->version >= FDC_82077 ) - outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS); + fd_outb(0x80 | ( FDCS->dtr &3), FD_STATUS); else { - outb_p(FDCS->dor & ~0x04, FD_DOR); + fd_outb(FDCS->dor & ~0x04, FD_DOR); udelay(FD_RESET_DELAY); - outb(FDCS->dor, FD_DOR); + fd_outb(FDCS->dor, FD_DOR); } } @@ -1794,12 +1769,12 @@ for(i=0; iaddress != -1){ printk("dor %d = %x\n", i, fdc_state[i].dor ); - outb_p(fdc_state[i].address+2, fdc_state[i].dor); + fd_outb(fdc_state[i].address+2, fdc_state[i].dor); udelay(1000); /* maybe we'll catch an interrupt... */ } } #endif - printk("status=%x\n", inb_p(FD_STATUS)); + printk("status=%x\n", fd_inb(FD_STATUS)); printk("fdc_busy=%d\n", fdc_busy); if( DEVICE_INTR) printk("DEVICE_INTR=%p\n", DEVICE_INTR); @@ -1828,7 +1803,7 @@ sti(); floppy_enable_hlt(); - disable_dma(FLOPPY_DMA); + fd_disable_dma(); /* avoid dma going to a random drive after shutdown */ if(!initialising) @@ -2411,10 +2386,14 @@ if ( ((unsigned long)buffer) % 512 ) DPRINT1("%p buffer not aligned\n", buffer); #endif - if ( CT(COMMAND) == FD_READ ) + if ( CT(COMMAND) == FD_READ ) { + fd_cacheflush(dma_buffer, size); memcpy( buffer, dma_buffer, size); - else + } + else { memcpy( dma_buffer, buffer, size); + fd_cacheflush(dma_buffer, size); + } remaining -= size; if ( !remaining) break; @@ -2862,6 +2841,9 @@ i = verify_area(VERIFY_WRITE,param,size); if (i) return i; + fd_cacheflush(address, size); /* is this necessary ??? */ + /* Ralf: Yes; only the l2 cache is completly chipset + controlled */ memcpy_tofs(param,(void *) address, size); return 0; } @@ -3920,6 +3902,8 @@ initialising=0; if(have_no_fdc) unregister_blkdev(MAJOR_NR,"fd"); + else + virtual_dma_init(); return have_no_fdc; } @@ -3939,28 +3923,27 @@ if(FDCS->address != -1){ fdc = i; reset_fdc_info(1); - outb_p(FDCS->dor, FD_DOR); + fd_outb(FDCS->dor, FD_DOR); } } set_dor(0, ~0, 8); /* avoid immediate interrupt */ - if (request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT|SA_SAMPLE_RANDOM, "floppy")) { + if (fd_request_irq()) { DPRINT1("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); return -1; } - if (request_dma(FLOPPY_DMA,"floppy")) { + if (fd_request_dma()) { DPRINT1("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA); - free_irq(FLOPPY_IRQ); + fd_free_irq(); return -1; } for(fdc = 0; fdc < N_FDC ; fdc++) if(FDCS->address != -1) - outb_p(FDCS->dor, FD_DOR); + fd_outb(FDCS->dor, FD_DOR); fdc = 0; - enable_irq(FLOPPY_IRQ); + fd_enable_irq(); return 0; } @@ -3981,10 +3964,10 @@ #ifdef FD_MODULE MOD_DEC_USE_COUNT; #endif - disable_dma(FLOPPY_DMA); - free_dma(FLOPPY_DMA); - disable_irq(FLOPPY_IRQ); - free_irq(FLOPPY_IRQ); + fd_disable_dma(); + fd_free_dma(); + fd_disable_irq(); + fd_free_irq(); set_dor(0, ~0, 8); #if N_FDC > 1 diff -u --recursive --new-file v1.3.35/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v1.3.35/linux/drivers/block/genhd.c Tue Oct 10 18:46:31 1995 +++ linux/drivers/block/genhd.c Fri Oct 20 10:10:39 1995 @@ -213,7 +213,8 @@ if (!tested_for_dm6++) { /* only check for DM6 *once* */ extern int ide_xlate_1024(kdev_t, int, const char *); /* check for various "disk managers" which do strange things */ - if (p->sys_ind == EZD_PARTITION) { + int ezstring = !strncmp(data+0x1a3, "Micro House", 11); + if (p->sys_ind == EZD_PARTITION || ezstring) { /* * The remainder of the disk must be accessed using * a translated geometry that reduces the number of diff -u --recursive --new-file v1.3.35/linux/drivers/block/gscd.c linux/drivers/block/gscd.c --- v1.3.35/linux/drivers/block/gscd.c Tue Oct 10 18:46:31 1995 +++ linux/drivers/block/gscd.c Thu Jan 1 02:00:00 1970 @@ -1,1126 +0,0 @@ -#define GSCD_VERSION "0.4a Oliver Raupach " - -/* - linux/drivers/block/gscd.c - GoldStar R420 CDROM driver - - Copyright (C) 1995 Oliver Raupach - based upon pre-works by Eberhard Moenkeberg - - - 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 - -#ifdef MODULE -#include -#include -#include -#ifndef CONFIG_MODVERSIONS -char kernel_version[] = UTS_RELEASE; -#endif -#endif MODULE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MAJOR_NR GOLDSTAR_CDROM_MAJOR -#include "blk.h" -#define gscd_port gscd /* for compatible parameter passing with "insmod" */ -#include - - -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 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 0 ); - - cmd_end (); - return; -} - -int find_drives (void) -{ -int *pdrv; -int drvnum; -int subdrv; -int i; - - speed = 0; - pdrv = (int *)&drv_states; - curr_drv_state = 0xFE; - subdrv = 0; - drvnum = 0; - - for ( i=0 ; i<8 ; i++ ) - { - subdrv++; - cmd_status (); - disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01; - if ( disk_state != (ST_x08 | ST_x04 | ST_INVALID) ) - { - /* LOC_240 */ - *pdrv = curr_drv_state; - init_cd_drive (drvnum); - pdrv++; - drvnum++; - } - else - { - if ( subdrv < 2 ) - { - continue; - } - else - { - subdrv = 0; - } - } - -/* curr_drv_state<<1; <-- das geht irgendwie nicht */ -/* muss heissen: curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */ - curr_drv_state *= 2; - curr_drv_state |= 1; -#ifdef GSCD_DEBUG - printk ("DriveState: %d\n", curr_drv_state ); -#endif - } - - ndrives = drvnum; - return drvnum; -} - -void init_cd_drive ( int num ) -{ -char resp [50]; -int i; - - printk ("GSCD: init unit %d\n", num ); - cc_Ident ((char *)&resp); - - printk ("GSCD: identification: "); - for ( i=0 ; i<0x1E; i++ ) - { - printk ( "%c", resp[i] ); - } - printk ("\n"); - - cc_SetSpeed (); - -} - -#ifdef FUTURE_WORK -/* return_done */ -static void update_state ( void ) -{ -unsigned int AX; - - - if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 ) - { - if ( disk_state == (ST_x08 | ST_x04 | ST_INVALID)) - { - AX = ST_INVALID; - } - - if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 ) - { - invalidate (); - f_drv_ok = 0; - } - - AX |= 0x8000; - } - - if ( disk_state & ST_PLAYING ) - { - AX |= 0x200; - } - - AX |= 0x100; - /* pkt_esbx = AX; */ - - disk_state = 0; - -} -#endif - -/* Init for the Module-Version */ -int init_module (void) -{ -long err; - - - /* call the GoldStar-init */ - err = my_gscd_init ( ); - - if ( err < 0 ) - { - return err; - } - else - { - printk ( "Happy GoldStar !\n" ); - return 0; - } -} - -#ifdef MODULE -void cleanup_module (void) -{ - - if (MOD_IN_USE) - { - printk("GoldStar-module in use - can't remove it.\n" ); - return; - } - - if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL)) - { - printk("What's that: can't unregister GoldStar-module\n" ); - return; - } - - release_region (gscd_port,4); - printk( "GoldStar-module released.\n" ); -} -#endif - - -/* Test for presence of drive and initialize it. Called only at boot time. */ -int gscd_init (void) -{ - return my_gscd_init (); -} - - -/* This is the common initalisation for the GoldStar drive. */ -/* It is called at boot time AND for module init. */ -int my_gscd_init (void) -{ -int i; -int result; - - printk ("GSCD: version %s\n", GSCD_VERSION); - printk ("GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", gscd_port); - - if (check_region(gscd_port, 4)) - { - printk("GSCD: Init failed, I/O port (%X) already in use.\n", gscd_port); - return -EIO; - } - - - /* check for card */ - result = wait_drv_ready (); - if ( result == 0x09 ) - { - printk ("GSCD: DMA kann ich noch nicht!\n" ); - return -EIO; - } - - if ( result == 0x0b ) - { - drv_mode = result; - i = find_drives (); - if ( i == 0 ) - { - printk ( "GSCD: GoldStar CD-ROM Drive is not found.\n" ); - return -EIO; - } - } - - if ( (result != 0x0b) && (result != 0x09) ) - { - printk ("GSCD: GoldStar Interface Adapter does not exist or H/W error\n" ); - return -EIO; - } - - /* reset all drives */ - i = 0; - while ( drv_states[i] != 0 ) - { - curr_drv_state = drv_states[i]; - printk ( "GSCD: Reset unit %d ... ",i ); - cc_Reset (); - printk ( "done\n" ); - i++; - } - - if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0) - { - printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n", - MAJOR_NR); - return -EIO; - } - - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; - read_ahead[MAJOR_NR] = 4; - - disk_state = 0; - gscdPresent = 1; - - request_region(gscd_port, 4, "gscd"); - - printk ( "GSCD: GoldStar CD-ROM Drive found.\n" ); - return 0; -} - -static void gscd_hsg2msf (long hsg, struct msf *msf) -{ - hsg += CD_BLOCK_OFFSET; - msf -> min = hsg / (CD_FRAMES*CD_SECS); - hsg %= CD_FRAMES*CD_SECS; - msf -> sec = hsg / CD_FRAMES; - msf -> frame = hsg % CD_FRAMES; - - gscd_bin2bcd(&msf -> min); /* convert to BCD */ - gscd_bin2bcd(&msf -> sec); - gscd_bin2bcd(&msf -> frame); -} - - -static void gscd_bin2bcd (unsigned char *p) -{ -int u, t; - - u = *p % 10; - t = *p / 10; - *p = u | (t << 4); -} - - -#ifdef FUTURE_WOTK -static long gscd_msf2hsg (struct msf *mp) -{ - return gscd_bcd2bin(mp -> frame) - + gscd_bcd2bin(mp -> sec) * CD_FRAMES - + gscd_bcd2bin(mp -> min) * CD_FRAMES * CD_SECS - - CD_BLOCK_OFFSET; -} - -static int gscd_bcd2bin (unsigned char bcd) -{ - return (bcd >> 4) * 10 + (bcd & 0xF); -} -#endif - - diff -u --recursive --new-file v1.3.35/linux/drivers/block/hd.c linux/drivers/block/hd.c --- v1.3.35/linux/drivers/block/hd.c Tue Oct 10 18:46:31 1995 +++ linux/drivers/block/hd.c Sat Oct 21 19:37:35 1995 @@ -42,7 +42,7 @@ #include #define MAJOR_NR HD_MAJOR -#include "blk.h" +#include static int revalidate_hddisk(kdev_t, int); diff -u --recursive --new-file v1.3.35/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v1.3.35/linux/drivers/block/ide.c Mon Oct 16 18:38:22 1995 +++ linux/drivers/block/ide.c Fri Oct 20 10:10:39 1995 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide.c Version 5.15 Oct 13, 1995 + * linux/drivers/block/ide.c Version 5.16 Oct 19, 1995 * * Copyright (C) 1994, 1995 Linus Torvalds & authors (see below) */ @@ -163,11 +163,16 @@ * don't enable 2nd CMD640 PCI port during init - conflict * Version 5.15 bug fix in init_cmd640_vlb() * bug fix in interrupt sharing code + * Version 5.16 ugh.. fix "serialize" support, broken in 5.15 + * remove "Huh?" from cmd640 code + * added qd6580 interface speed select from Colten Edwards * * Driver compile-time options are in ide.h * * To do, in likely order of completion: - * - add ioctls to get/set interface timings on cmd640, ht6560b, triton + * - add ALI M1443/1445 chipset support from derekn@vw.ece.cmu.edu + * - add Promise Caching controller support from peterd@pnd-pc.demon.co.uk + * - add ioctls to get/set interface timings on various interfaces * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f * - improved CMD support: handed this off to someone else * - find someone to work on IDE *tape drive* support @@ -209,7 +214,7 @@ static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 0x168}; static const byte default_irqs[MAX_HWIFS] = {14, 15, 11, 10}; -static int single_threaded = 0; /* "serialize" option */ +static int serialized = 0; /* "serialize" option */ static int disallow_unmask = 0; /* for buggy hardware */ #if (DISK_RECOVERY_TIME > 0) @@ -2360,6 +2365,9 @@ static void init_dtc2278 (void) { + unsigned long flags; + + save_flags(flags); cli(); #if SET_DTC2278_MODE4 /* @@ -2375,11 +2383,39 @@ inb(0x3f6); outb_p(0x20,0xb4); inb(0x3f6); - - sti(); + restore_flags(flags); } #endif /* SUPPORT_DTC2278 */ +#ifdef SUPPORT_QD6580 +/* + * QDI QD6580 EIDE controller fast support by Colten Edwards. + * no net access but I can be reached at pje120@cs.usask.ca + * + * I suppose that a IOCTL could be used for this and other + * cards like it to modify the speed using hdparm. Someday.. + */ +static void init_qd6580 (void) +{ + unsigned long flags; + + /* looks like 0x4f is fast + * 0x3f is medium + * 0x2f is slower + * 0x1f is slower yet + * ports are 0xb0 0xb2 and 0xb3 + */ + + save_flags(flags); + cli(); + outb_p(0x8d,0xb0); + outb_p(0x0 ,0xb2); + outb_p(0x4f,0xb3); /* select "fast" 0x4f */ + inb(0x3f6); + restore_flags(flags); +} +#endif /* SUPPORT_QD6580 */ + #if SUPPORT_CMD640 /* * ??? fixme: @@ -2412,7 +2448,7 @@ byte reg; unsigned short port = 0x178; - single_threaded = 1; + serialized = 1; printk("ide: buggy CMD640 interface: serialized, "); reg = read_cmd640_vlb(port, 0x50); if (reg == 0xff || (reg & 0x90) != 0x90) { @@ -2523,7 +2559,12 @@ * "idex=dtc2278" : enables use of DTC2278 secondary i/f * "idex=ht6560b" : enables use of HT6560B secondary i/f * "idex=cmd640_vlb" : required for VLB cards with the CMD640 chip - * (PCI version will be automatically detected) + * (not for PCI -- automatically detected) + * + * This option is valid ONLY on ide0, and the defaults for the base,ctl ports + * must not be altered. + * + * "ide0=qd6580" : select "fast" interface speed on a qd6580 interface */ void ide_setup (char *s) { @@ -2561,7 +2602,7 @@ hwif->noprobe = 0; goto done; case -4: /* "serialize" */ - printk(" -- USE ""ide%c=serialize"" INSTEAD", '0'+hw); + printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw); goto do_serialize; case 3: /* cyl,head,sect */ drive->media = disk; @@ -2579,11 +2620,17 @@ * Look for interface options: "idex=" */ if (s[0] == 'i' && s[1] == 'd' && s[2] == 'e' && s[3] >= '0' && s[3] <= max_hwif) { - const char *ide_words[] = {"noprobe", "serialize", "dtc2278", "ht6560b", "cmd640_vlb", NULL}; + const char *ide_words[] = {"noprobe", "serialize", "dtc2278", "ht6560b", "cmd640_vlb", "qd6580", NULL}; hw = s[3] - '0'; hwif = &ide_hwifs[hw]; switch (match_parm(&s[4], ide_words, vals, 3)) { +#if SUPPORT_QD6580 + case -6: /* "qd6580" */ + if (hw != 0) goto bad_hwif; + init_qd6580(); + goto done; +#endif /* SUPPORT_QD6580 */ #if SUPPORT_CMD640 case -5: /* "cmd640_vlb" */ if (hw > 1) goto bad_hwif; @@ -2618,7 +2665,7 @@ case -2: /* "serialize" */ do_serialize: if (hw > 1) goto bad_hwif; - single_threaded = 1; + serialized = 1; goto done; case -1: /* "noprobe" */ hwif->noprobe = 1; @@ -2747,26 +2794,34 @@ static int init_irq (ide_hwif_t *hwif) { unsigned long flags; - ide_hwgroup_t *hwgroup; + int irq = hwif->irq; + ide_hwgroup_t *hwgroup = irq_to_hwgroup[irq]; - /* - * First, we try to grab the irq - */ save_flags(flags); cli(); - if ((hwgroup = irq_to_hwgroup[hwif->irq]) == NULL) { - if (request_irq(hwif->irq, ide_intr, - SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name)) { + + /* + * Grab the irq if we don't already have it from a previous hwif + */ + if (hwgroup == NULL) { + if (request_irq(irq, ide_intr, SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name)) { restore_flags(flags); printk(" -- FAILED!"); return 1; } - - /* - * Got the irq, now set everything else up - */ + } + /* + * Check for serialization with ide1. + * This code depends on us having already taken care of ide1. + */ + if (serialized && hwif->name[3] == '0' && ide_hwifs[1].present) + hwgroup = ide_hwifs[1].hwgroup; + /* + * If this is the first interface in a group, + * then we need to create the hwgroup structure + */ + if (hwgroup == NULL) { hwgroup = kmalloc (sizeof(ide_hwgroup_t), GFP_KERNEL); - irq_to_hwgroup[hwif->irq] = hwgroup; hwgroup->hwif = hwif->next = hwif; hwgroup->rq = NULL; hwgroup->handler = NULL; @@ -2783,18 +2838,14 @@ hwgroup->hwif->next = hwif; } hwif->hwgroup = hwgroup; + irq_to_hwgroup[irq] = hwgroup; restore_flags(flags); /* safe now that hwif->hwgroup is set up */ printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name, - hwif->io_base, hwif->io_base+7, hwif->ctl_port, hwif->irq); - if (hwgroup->hwif != hwif) { - char *name = hwgroup->hwif->name; - if (hwgroup->hwif->irq == hwif->irq) - printk(" (shared with %s)", name); - else - printk(" (serialized with %s)", name); - } + hwif->io_base, hwif->io_base+7, hwif->ctl_port, irq); + if (hwgroup->hwif != hwif) + printk(" (serialized with %s)", hwgroup->hwif->name); printk("\n"); return 0; } @@ -2826,7 +2877,7 @@ void buggy_interface_fallback (int rc) { ide_pci_access_error (rc); - single_threaded = 1; + serialized = 1; disallow_unmask = 1; printk("serialized, disabled unmasking\n"); } @@ -2859,7 +2910,7 @@ int rc; unsigned char reg; - single_threaded = 1; + serialized = 1; printk("ide: buggy CMD640 interface: "); #if 0 /* funny.. the cmd640b I tried this on claimed to not be enabled.. */ @@ -2869,7 +2920,6 @@ } else if (!(sreg & 1)) { printk("not enabled\n"); } else { -#endif /* 0 */ /* * The first part is undocumented magic from the DOS driver. @@ -2879,6 +2929,7 @@ if (pcibios_write_config_byte(bus, fn, 0x5b, 0xbd) != 0xbd) printk("init_cmd640: huh? 0x5b read back wrong\n"); (void) pcibios_write_config_byte(bus, fn, 0x5b, 0); +#endif /* 0 */ /* * The rest is from the cmd640b datasheet. */ @@ -2988,15 +3039,6 @@ if (!hwif->present) continue; hwif->present = 0; /* we set it back to 1 if all is ok below */ - if (h == 0 && single_threaded) { - if (ide_hwifs[1].present) { - if (irq_to_hwgroup[hwif->irq] != NULL) { - printk("%s: SERIALIZE BUG!\n", hwif->name); - continue; - } - irq_to_hwgroup[hwif->irq] = irq_to_hwgroup[ide_hwifs[1].irq]; - } - } switch (hwif->major) { case IDE0_MAJOR: rfn = &do_ide0_request; break; case IDE1_MAJOR: rfn = &do_ide1_request; break; diff -u --recursive --new-file v1.3.35/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v1.3.35/linux/drivers/block/ide.h Mon Sep 18 14:53:50 1995 +++ linux/drivers/block/ide.h Mon Oct 23 13:42:32 1995 @@ -41,6 +41,9 @@ #ifndef SUPPORT_HT6560B /* 1 to support HT6560B chipset */ #define SUPPORT_HT6560B 1 /* 0 to reduce kernel size */ #endif +#ifndef SUPPORT_QD6580 /* 1 to support QD6580 chipset */ +#define SUPPORT_QD6580 1 /* 0 to reduce kernel size */ +#endif #ifndef SUPPORT_DTC2278 /* 1 to support DTC2278 chipset */ #define SUPPORT_DTC2278 1 /* 0 to reduce kernel size */ #ifndef SET_DTC2278_MODE4 @@ -350,7 +353,7 @@ * One final include file, which references some of the data/defns from above */ #define IDE_DRIVER /* "parameter" for blk.h */ -#include "blk.h" +#include #if (DISK_RECOVERY_TIME > 0) void ide_set_recovery_timer (ide_hwif_t *); diff -u --recursive --new-file v1.3.35/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v1.3.35/linux/drivers/block/ll_rw_blk.c Tue Oct 10 18:46:32 1995 +++ linux/drivers/block/ll_rw_blk.c Sat Oct 21 19:37:35 1995 @@ -19,7 +19,7 @@ #include #include -#include "blk.h" +#include /* * The request-struct contains all necessary data diff -u --recursive --new-file v1.3.35/linux/drivers/block/mcd.c linux/drivers/block/mcd.c --- v1.3.35/linux/drivers/block/mcd.c Tue Oct 10 18:46:32 1995 +++ linux/drivers/block/mcd.c Thu Jan 1 02:00:00 1970 @@ -1,1632 +0,0 @@ -/* - linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver - - Copyright (C) 1992 Martin Harriss - - martin@bdsi.com (no longer valid - where are you now, Martin?) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - HISTORY - - 0.1 First attempt - internal use only - 0.2 Cleaned up delays and use of timer - alpha release - 0.3 Audio support added - 0.3.1 Changes for mitsumi CRMC LU005S march version - (stud11@cc4.kuleuven.ac.be) - 0.3.2 bug fixes to the ioctls and merged with ALPHA0.99-pl12 - (Jon Tombs ) - 0.3.3 Added more #defines and mcd_setup() - (Jon Tombs ) - - 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 - 07 July 1995 Modifications by Andrew J. Kroll - -*/ - -#include - -#ifdef MODULE -# include -# include -# 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* #define REALLY_SLOW_IO */ -#include -#include -#include - -#define MAJOR_NR MITSUMI_CDROM_MAJOR -#include "blk.h" -#define mcd_port mcd /* for compatible parameter passing with "insmod" */ -#include - -#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 /* */ -#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("", st); -#endif - if (!(st & MFL_STATUS)) - { - st = inb(MCDPORT(0)) & 0xFF; -#ifdef TEST1 - printk("", 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 diff -u --recursive --new-file v1.3.35/linux/drivers/block/mcdx.c linux/drivers/block/mcdx.c --- v1.3.35/linux/drivers/block/mcdx.c Tue Oct 10 18:46:32 1995 +++ linux/drivers/block/mcdx.c Thu Jan 1 02:00:00 1970 @@ -1,1727 +0,0 @@ -/* - * The Mitsumi CDROM interface - * Copyright (C) 1995 Heiko Schlittermann - * VERSION: 1.0a - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Thanks to - * The Linux Community at all and ... - * Martin Harriss (he wrote the first Mitsumi Driver) - * Eberhard Moenkeberg (he gave me much support and the initial kick) - * Bernd Huebner, Ruediger Helsch (Unifix-Software GmbH, they - * improved the original driver) - * Jon Tombs, Bjorn Ekwall (module support) - * Daniel v. Mosnenck (he sent me the Technical and Programming Reference) - * Gerd Knorr (he lent me his PhotoCD) - * Nils Faerber and Roger E. Wolff (extensivly tested the LU portion) - * ... somebody forgotten? - * - */ - - -#if RCS -static const char *mcdx_c_version - = "mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp"; -#endif - -#include -#ifdef MODULE -#include -#include -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include - -#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 - -/* CONSTANTS *******************************************************/ - -const int REQUEST_SIZE = 200; -const int DIRECT_SIZE = 200; - -enum drivemodes { TOC, DATA, RAW, COOKED }; -enum datamodes { MODE0, MODE1, MODE2 }; -enum resetmodes { SOFT, HARD }; - -const int SINGLE = 0x01; -const int DOUBLE = 0x02; -const int DOOR = 0x04; -const int MULTI = 0x08; -const int READY = 0x70; - -const unsigned char READSSPEED = 0xc0; -const unsigned char READDSPEED = 0xc1; - - -/* DECLARATIONS ****************************************************/ -struct s_msf { - unsigned char minute; - unsigned char second; - unsigned char frame; -}; - -struct s_subqcode { - unsigned char control; - unsigned char tno; - unsigned char index; - struct s_msf tt; - struct s_msf dt; -}; - -struct s_diskinfo { - unsigned int n_first; - unsigned int n_last; - struct s_msf msf_leadout; - struct s_msf msf_first; -}; - -struct s_multi { - unsigned char multi; - struct s_msf msf_last; -}; - -struct s_version { - unsigned char code; - unsigned char ver; -}; - -/* Per drive/controller stuff **************************************/ - -struct s_drive_stuff { - /* waitquenes */ - struct wait_queue *busyq; - struct wait_queue *lockq; - struct wait_queue *sleepq; - - /* flags */ - volatile int introk; /* status of last irq operation */ - volatile int busy; /* drive performs an operation */ - volatile int lock; /* exclusive usage */ - - /* cd infos */ - struct s_diskinfo di; - struct s_multi multi; - struct s_subqcode* toc; /* first enty of the toc array */ - struct s_subqcode start; - struct s_subqcode stop; - int xa; /* 1 if xa disk */ - int audio; /* 1 if audio disk */ - int audiostatus; - - /* `buffer' control */ - volatile int valid; - volatile int pending; - volatile int off_direct; - volatile int off_requested; - - /* adds and odds */ - void* wreg_data; /* w data */ - void* wreg_reset; /* w hardware reset */ - void* wreg_hcon; /* w hardware conf */ - void* wreg_chn; /* w channel */ - void* rreg_data; /* r data */ - void* rreg_status; /* r status */ - - int irq; /* irq used by this drive */ - int minor; /* minor number of this drive */ - int present; /* drive present and its capabilities */ - char readcmd; /* read cmd depends on single/double speed */ - char playcmd; /* play should always be single speed */ - unsigned long changed; /* last jiff the media was changed */ - unsigned long xxx; /* last jiff it was asked for media change */ - int users; /* keeps track of open/close */ - int lastsector; /* last block accessible */ - int errno; /* last operation's error */ - -}; - - -/* Prototypes ******************************************************/ - -/* The following prototypes are already declared elsewhere. They are - repeated here to show what's going on. And to sense, if they're - changed elsewhere. */ - -/* declared in blk.h */ -int mcdx_init(void); -void do_mcdx_request(void); - -int check_mcdx_media_change(kdev_t); - -/* already declared in init/main */ -void mcdx_setup(char *, int *); - -/* Indirect exported functions. These functions are exported by their - addresses, such as mcdx_open and mcdx_close in the - structure fops. */ - -/* ??? exported by the mcdx_sigaction struct */ -static void mcdx_intr(int, struct pt_regs*); - -/* exported by file_ops */ -static int mcdx_open(struct inode*, struct file*); -static void mcdx_close(struct inode*, struct file*); -static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long); - -/* misc internal support functions */ -static void log2msf(unsigned int, struct s_msf*); -static unsigned int msf2log(const struct s_msf*); -static unsigned int uint2bcd(unsigned int); -static unsigned int bcd2uint(unsigned char); -#if MCDX_DEBUG -static void TRACE((int level, const char* fmt, ...)); -#endif -static void warn(const char* fmt, ...); -static char *port(int*); -static int irq(int*); -static void mcdx_delay(struct s_drive_stuff*, long jifs); -static int mcdx_transfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors); - -static int mcdx_config(struct s_drive_stuff*, int); -static int mcdx_closedoor(struct s_drive_stuff*, int); -static int mcdx_requestversion(struct s_drive_stuff*, struct s_version*, int); -static int mcdx_lockdoor(struct s_drive_stuff*, int, int); -static int mcdx_stop(struct s_drive_stuff*, int); -static int mcdx_hold(struct s_drive_stuff*, int); -static int mcdx_reset(struct s_drive_stuff*, enum resetmodes, int); -static int mcdx_eject(struct s_drive_stuff*, int); -static int mcdx_setdrivemode(struct s_drive_stuff*, enum drivemodes, int); -static int mcdx_setdatamode(struct s_drive_stuff*, enum datamodes, int); -static int mcdx_requestsubqcode(struct s_drive_stuff*, struct s_subqcode*, int); -static int mcdx_requestmultidiskinfo(struct s_drive_stuff*, struct s_multi*, int); -static int mcdx_requesttocdata(struct s_drive_stuff*, struct s_diskinfo*, int); -static int mcdx_getstatus(struct s_drive_stuff*, int); -static int mcdx_getval(struct s_drive_stuff*, int to, int delay, char*); - -static int mcdx_talk(struct s_drive_stuff*, - const unsigned char* cmd, size_t, void *buffer, - size_t size, unsigned int timeout, int); -static int mcdx_readtoc(struct s_drive_stuff*); -static int mcdx_playtrk(struct s_drive_stuff*, const struct cdrom_ti*); -static int mcdx_playmsf(struct s_drive_stuff*, const struct cdrom_msf*); - -/* static variables ************************************************/ - -static int dummy0; -static int mcdx_drive_map[][2] = MCDX_DRIVEMAP; -static struct s_drive_stuff* mcdx_stuffp[MCDX_NDRIVES]; -static struct s_drive_stuff* mcdx_irq_map[16] = - {0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0}; - -static struct file_operations mcdx_fops = { - NULL, /* lseek - use kernel default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* no readdir */ - NULL, /* no select */ - mcdx_ioctl, /* ioctl() */ - NULL, /* no mmap */ - mcdx_open, /* open() */ - mcdx_close, /* close() */ - NULL, /* fsync */ - NULL, /* fasync */ - check_mcdx_media_change, /* media_change */ - NULL /* revalidate */ -}; - -/* KERNEL INTERFACE FUNCTIONS **************************************/ - -static int -mcdx_ioctl( - struct inode* ip, struct file* fp, - unsigned int cmd, unsigned long arg) -{ - struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; - - if (!stuffp->present) return -ENXIO; - if (!ip) return -EINVAL; - - switch (cmd) { - case CDROMSTART: { - TRACE((IOCTL, "ioctl() START\n")); - return 0; - } - - case CDROMSTOP: { - TRACE((IOCTL, "ioctl() STOP\n")); - stuffp->audiostatus = CDROM_AUDIO_INVALID; - if (-1 == mcdx_stop(stuffp, 1)) - return -EIO; - return 0; - } - - case CDROMPLAYTRKIND: { - int ans; - struct cdrom_ti ti; - - TRACE((IOCTL, "ioctl() PLAYTRKIND\n")); - if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti)))) - return ans; - memcpy_fromfs(&ti, (void*) arg, sizeof(ti)); - if ((ti.cdti_trk0 < stuffp->di.n_first) - || (ti.cdti_trk0 > stuffp->di.n_last) - || (ti.cdti_trk1 < stuffp->di.n_first)) - return -EINVAL; - if (ti.cdti_trk1 > stuffp->di.n_last) ti.cdti_trk1 = stuffp->di.n_last; - TRACE((IOCTL, "ioctl() track %d to %d\n", ti.cdti_trk0, ti.cdti_trk1)); - - return mcdx_playtrk(stuffp, &ti); - } - - case CDROMPLAYMSF: { - int ans; - struct cdrom_msf msf; - - TRACE((IOCTL, "ioctl() PLAYMSF\n")); - - if ((stuffp->audiostatus == CDROM_AUDIO_PLAY) - && (-1 == mcdx_hold(stuffp, 1))) return -EIO; - - if ((ans = verify_area( - VERIFY_READ, (void*) arg, sizeof(struct cdrom_msf)))) - return ans; - - memcpy_fromfs(&msf, (void*) arg, sizeof msf); - - msf.cdmsf_min0 = uint2bcd(msf.cdmsf_min0); - msf.cdmsf_sec0 = uint2bcd(msf.cdmsf_sec0); - msf.cdmsf_frame0 = uint2bcd(msf.cdmsf_frame0); - - msf.cdmsf_min1 = uint2bcd(msf.cdmsf_min1); - msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1); - msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1); - - return mcdx_playmsf(stuffp, &msf); - } - - case CDROMRESUME: { - TRACE((IOCTL, "ioctl() RESUME\n")); - return mcdx_playtrk(stuffp, NULL); - } - - case CDROMREADTOCENTRY: { - struct cdrom_tocentry entry; - struct s_subqcode *tp = NULL; - int ans; - - TRACE((IOCTL, "ioctl() READTOCENTRY\n")); - - if (-1 == mcdx_readtoc(stuffp)) return -1; - - if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(entry)))) return ans; - memcpy_fromfs(&entry, (void *) arg, sizeof(entry)); - - if (entry.cdte_track == CDROM_LEADOUT) - tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1]; - else if (entry.cdte_track > stuffp->di.n_last - || entry.cdte_track < stuffp->di.n_first) return -EINVAL; - else tp = &stuffp->toc[entry.cdte_track - stuffp->di.n_first]; - - if (NULL == tp) WARN(("FATAL.\n")); - - entry.cdte_adr = tp->control; - entry.cdte_ctrl = tp->control >> 4; - - if (entry.cdte_format == CDROM_MSF) { - entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute); - entry.cdte_addr.msf.second = bcd2uint(tp->dt.second); - entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame); - } else if (entry.cdte_format == CDROM_LBA) - entry.cdte_addr.lba = msf2log(&tp->dt); - else return -EINVAL; - - if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(entry)))) return ans; - memcpy_tofs((void*) arg, &entry, sizeof(entry)); - - return 0; - } - - case CDROMSUBCHNL: { - int ans; - struct cdrom_subchnl sub; - struct s_subqcode q; - - TRACE((IOCTL, "ioctl() SUBCHNL\n")); - - if ((ans = verify_area(VERIFY_READ, - (void*) arg, sizeof(sub)))) return ans; - - memcpy_fromfs(&sub, (void*) arg, sizeof(sub)); - - if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO; - - TRACE((IOCTL, "audiostatus: %x\n", stuffp->audiostatus)); - sub.cdsc_audiostatus = stuffp->audiostatus; - sub.cdsc_adr = q.control; - sub.cdsc_ctrl = q.control >> 4; - sub.cdsc_trk = bcd2uint(q.tno); - sub.cdsc_ind = bcd2uint(q.index); - - TRACE((IOCTL, "trk %d, ind %d\n", - sub.cdsc_trk, sub.cdsc_ind)); - - if (sub.cdsc_format == CDROM_LBA) { - sub.cdsc_absaddr.lba = msf2log(&q.dt); - sub.cdsc_reladdr.lba = msf2log(&q.tt); - TRACE((IOCTL, "lba: abs %d, rel %d\n", - sub.cdsc_absaddr.lba, - sub.cdsc_reladdr.lba)); - } else if (sub.cdsc_format == CDROM_MSF) { - sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute); - sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second); - sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame); - sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute); - sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second); - sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame); - TRACE((IOCTL, - "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n", - sub.cdsc_absaddr.msf.minute, - sub.cdsc_absaddr.msf.second, - sub.cdsc_absaddr.msf.frame, - sub.cdsc_reladdr.msf.minute, - sub.cdsc_reladdr.msf.second, - sub.cdsc_reladdr.msf.frame)); - } else return -EINVAL; - - if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(sub)))) - return ans; - memcpy_tofs((void*) arg, &sub, sizeof(sub)); - - return 0; - } - - case CDROMREADTOCHDR: { - struct cdrom_tochdr toc; - int ans; - - TRACE((IOCTL, "ioctl() READTOCHDR\n")); - if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof toc))) - return ans; - toc.cdth_trk0 = stuffp->di.n_first; - toc.cdth_trk1 = stuffp->di.n_last; - memcpy_tofs((void*) arg, &toc, sizeof toc); - TRACE((IOCTL, "ioctl() track0 = %d, track1 = %d\n", - stuffp->di.n_first, stuffp->di.n_last)); - return 0; - } - - case CDROMPAUSE: { - TRACE((IOCTL, "ioctl() PAUSE\n")); - if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL; - if (-1 == mcdx_stop(stuffp, 1)) return -EIO; - if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1)) - return -EIO; - - stuffp->audiostatus = CDROM_AUDIO_PAUSED; - return 0; - } - - case CDROMMULTISESSION: { - int ans; - struct cdrom_multisession ms; - TRACE((IOCTL, "ioctl() MULTISESSION\n")); - if (0 != (ans = verify_area(VERIFY_READ, (void*) arg, - sizeof(struct cdrom_multisession)))) - return ans; - - memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession)); - if (ms.addr_format == CDROM_MSF) { - ms.addr.msf.minute = bcd2uint(stuffp->multi.msf_last.minute); - ms.addr.msf.second = bcd2uint(stuffp->multi.msf_last.second); - ms.addr.msf.frame = bcd2uint(stuffp->multi.msf_last.frame); - } else if (ms.addr_format == CDROM_LBA) - ms.addr.lba = msf2log(&stuffp->multi.msf_last); - else - return -EINVAL; - ms.xa_flag = stuffp->xa; - - if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg, - sizeof(struct cdrom_multisession)))) - return ans; - - memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession)); - if (ms.addr_format == CDROM_MSF) - TRACE((IOCTL, - "ioctl() (%d, %02x:%02x.%02x [%02x:%02x.%02x])\n", - ms.xa_flag, - ms.addr.msf.minute, - ms.addr.msf.second, - ms.addr.msf.frame, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame)); - else - { - dummy0=0; - TRACE((IOCTL, - "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n", - ms.xa_flag, - ms.addr.lba, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame)); - } - return 0; - } - - case CDROMEJECT: { - TRACE((IOCTL, "ioctl() EJECT\n")); - if (stuffp->users > 1) return -EBUSY; - if (-1 == mcdx_eject(stuffp, 1)) return -EIO; - return 0; - } - - case CDROMVOLCTRL: { - TRACE((IOCTL, "ioctl() volctrl\n")); - return 0; - } - - default: - WARN(("ioctl(): unknown request 0x%04x\n", cmd)); - return -EINVAL; - } -} - -void do_mcdx_request() -{ - int dev; - struct s_drive_stuff *stuffp; - - again: - - TRACE((REQUEST, "do_request()\n")); - - if ((CURRENT == NULL) || (CURRENT->rq_status == RQ_INACTIVE)) { - TRACE((REQUEST, "do_request() done\n")); - return; - } - - stuffp = mcdx_stuffp[MINOR(CURRENT->rq_dev)]; - TRACE((REQUEST, "do_request() stuffp = %p\n", stuffp)); - - INIT_REQUEST; - dev = MINOR(CURRENT->rq_dev); - - if ((dev < 0) || (dev >= MCDX_NDRIVES) || (!stuffp->present)) { - WARN(("do_request(): bad device: %s\n", - kdevname(CURRENT->rq_dev))); - end_request(0); - goto again; - } - - if (stuffp->audio) { - WARN(("do_request() attempt to read from audio cd\n")); - end_request(0); - goto again; - } - - switch (CURRENT->cmd) { - case WRITE: - WARN(("do_request(): attempt to write to cd!!\n")); - end_request(0); - break; - - case READ: - stuffp->errno = 0; - while (CURRENT->nr_sectors) { - int i; - - if (-1 == (i = mcdx_transfer( - stuffp, - CURRENT->buffer, - CURRENT->sector, - CURRENT->nr_sectors))) { - WARN(("do_request() read error\n")); - if (stuffp->errno == MCDX_EOM) { - CURRENT->sector += CURRENT->nr_sectors; - CURRENT->nr_sectors = 0; - } - end_request(0); - goto again; - } - CURRENT->sector += i; - CURRENT->nr_sectors -= i; - CURRENT->buffer += (i * 512); - - } - - end_request(1); - break; - - default: - panic(MCDX "do_request: unknown command.\n"); - break; - } - - goto again; -} - -static int -mcdx_open(struct inode *ip, struct file *fp) -{ - struct s_drive_stuff *stuffp; - - TRACE((OPENCLOSE, "open()\n")); - stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; - if (!stuffp->present) return -ENXIO; - - if (-1 == mcdx_getstatus(stuffp, 1)) return -EIO; - - /* close the door, if necessary (get the door information - from the hardware status register) */ - if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR) - mcdx_closedoor(stuffp, 1); - - /* if the media changed we will have to little more */ - if (stuffp->xxx < stuffp->changed) { - - TRACE((OPENCLOSE, "open() media changed\n")); - /* but wait - the time of media change will be set at the - very last of this block - it seems, some of the following - talk() will detect a media change ... (I think, config() - is the reason. */ - - /* - TRACE((OPENCLOSE, "open() hardware reset\n")); - if (-1 == mcdx_reset(stuffp, HARD, 1)) return -EIO; - */ - - stuffp->audiostatus = CDROM_AUDIO_INVALID; - - /* get the multisession information */ - { - TRACE((OPENCLOSE, "open() Request multisession info\n")); - if (-1 == mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6)) - return -EIO; - - if (stuffp->multi.multi > 2) - WARN(("open() unknown multisession value (%d)\n", stuffp->multi.multi)); - - /* multisession ? */ - if (!stuffp->multi.multi) - stuffp->multi.msf_last.second = 2; - - TRACE((OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n", - stuffp->multi.multi, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame)); - } /* got multisession information */ - - /* request the disks table of contents (aka diskinfo) */ - if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) return -EIO; - - stuffp->lastsector = (CD_FRAMESIZE / 512) - * msf2log(&stuffp->di.msf_leadout) - 1; - - TRACE((OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n", - stuffp->di.n_first, - stuffp->di.msf_first.minute, - stuffp->di.msf_first.second, - stuffp->di.msf_first.frame, - msf2log(&stuffp->di.msf_first))); - TRACE((OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n", - stuffp->di.n_last, - stuffp->di.msf_leadout.minute, - stuffp->di.msf_leadout.second, - stuffp->di.msf_leadout.frame, - msf2log(&stuffp->di.msf_leadout))); - - if (stuffp->toc) { - TRACE((MALLOC, "open() free toc @ %p\n", stuffp->toc)); - kfree(stuffp->toc); - } - stuffp->toc = NULL; - - TRACE((OPENCLOSE, "open() init irq generation\n")); - if (-1 == mcdx_config(stuffp, 1)) return -EIO; - - /* try to get the first sector ... */ - { - char buf[512]; - int ans; - int tries; - - stuffp->xa = 0; - stuffp->audio = 0; - - for (tries = 6; tries; tries--) { - TRACE((OPENCLOSE, "open() try as %s\n", - stuffp->xa ? "XA" : "normal")); - - /* set data mode */ - if (-1 == (ans = mcdx_setdatamode(stuffp, - stuffp->xa ? MODE2 : MODE1, 1))) - return -EIO; - - if ((stuffp->audio = e_audio(ans))) break; - - while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) - ; - - if (ans == 1) break; - stuffp->xa = !stuffp->xa; - } - if (!tries) return -EIO; - } - - /* xa disks will be read in raw mode, others not */ - if (-1 == mcdx_setdrivemode(stuffp, - stuffp->xa ? RAW : COOKED, 1)) - return -EIO; - - if (stuffp->audio) { - INFO(("open() audio disk found\n")); - } else { - INFO(("open() %s%s disk found\n", - stuffp->xa ? "XA / " : "", - stuffp->multi.multi ? "Multi Session" : "Single Session")); - } - - stuffp->xxx = jiffies; - } - - /* lock the door if not already done */ - if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1))) - return -EIO; - - stuffp->users++; - MOD_INC_USE_COUNT; - return 0; -} - -static void -mcdx_close(struct inode *ip, struct file *fp) -{ - struct s_drive_stuff *stuffp; - - TRACE((OPENCLOSE, "close()\n")); - - stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; - - if (0 == --stuffp->users) { - sync_dev(ip->i_rdev); /* needed for r/o device? */ - - /* invalidate_inodes(ip->i_rdev); */ - invalidate_buffers(ip->i_rdev); - - if (-1 == mcdx_lockdoor(stuffp, 0, 1)) - printk(MCDX ": Cannot unlock the door\n"); - } - MOD_DEC_USE_COUNT; - - return; -} - -int check_mcdx_media_change(kdev_t full_dev) -/* Return: 1 if media changed since last call to - this function - 0 else - Setting flag to 0 resets the changed state. */ - -{ - INFO(("check_mcdx_media_change called for device %s\n", - kdevname(full_dev))); - return 0; -} - -void mcdx_setup(char *str, int *pi) -{ -#if MCDX_DEBUG - printk(MCDX ":: setup(%s, %d) called\n", - str, pi[0]); -#endif -} - -/* DIRTY PART ******************************************************/ - -static void mcdx_delay(struct s_drive_stuff *stuff, long jifs) -/* This routine is used for sleeping while initialisation - it seems that - there are no other means available. May be we could use a simple count - loop w/ jumps to itself, but I wanna make this independend of cpu - speed. [1 jiffie is 1/HZ sec */ -{ - unsigned long tout = jiffies + jifs; - - TRACE((INIT, "mcdx_delay %d\n", jifs)); - if (jifs < 0) return; - -#if 1 - while (jiffies < tout) { - current->timeout = jiffies; - schedule(); - } -#else - if (current->pid == 0) { /* no sleep allowed */ - while (jiffies < tout) { - current->timeout = jiffies; - schedule(); - } - } else { /* sleeping is allowed */ - current->timeout = tout; - current->state = TASK_INTERRUPTIBLE; - while (current->timeout) { - interruptible_sleep_on(&stuff->sleepq); - } - } -#endif -} - -static void -mcdx_intr(int irq, struct pt_regs* regs) -{ - struct s_drive_stuff *stuffp; - unsigned char x; - - stuffp = mcdx_irq_map[irq]; - - if (!stuffp->busy) { - INFO(("intr() unexpected interrupt @ irq %d\n", irq)); - return; - } - - /* if not ok read the next byte as the drives status */ - if (0 == (stuffp->introk = - (~(x = inb((unsigned int) stuffp->rreg_status)) & MCDX_RBIT_DTEN))) - TRACE((IRQ, "intr() irq %d failed, status %02x %02x\n", - irq, x, inb((unsigned int) stuffp->rreg_data))); - else - { - dummy0=0; - TRACE((IRQ, "irq() irq %d ok, status %02x\n", irq, x)); - } - stuffp->busy = 0; - wake_up_interruptible(&stuffp->busyq); -} - - -static int -mcdx_talk ( - struct s_drive_stuff *stuffp, - const unsigned char *cmd, size_t cmdlen, - void *buffer, size_t size, - unsigned int timeout, int tries) -/* Send a command to the drive, wait for the result. - * returns -1 on timeout, drive status otherwise - */ -{ - char c; - int st; - - if (!buffer || size == 0) buffer = &c, size = 1; - - while (stuffp->lock) - interruptible_sleep_on(&stuffp->lockq); - - if (current->signal && ~current->blocked) { - WARN(("talk() got signal %d\n", current->signal)); - return -1; - } - - stuffp->lock = 1; - stuffp->valid = 0; - -#if MCDX_DEBUG & TALK - { - unsigned char i; - TRACE((TALK, "talk() %d / %d tries, res.size %d, command 0x%02x", - tries, timeout, size, (unsigned char) cmd[0])); - for (i = 1; i < cmdlen; i++) printk(" 0x%02x", cmd[i]); - printk("\n"); - } -#endif - - /* give up if all tries are done (bad) or if the status - * st != -1 (good) */ - for (st = -1; st == -1 && tries; tries--) { - - size_t sz = size; - char* bp = buffer; - - outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen); - TRACE((TALK, "talk() command sent\n")); - - /* get the status byte */ - if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) { - WARN(("talk() %02x timed out (status), %d tr%s left\n", - cmd[0], tries - 1, tries == 2 ? "y" : "ies")); - continue; - } - st = *bp++; - sz--; - - TRACE((TALK, "talk() got status 0x%02x\n", st)); - - /* command error? */ - if (e_cmderr(st)) { - WARN(("command error %02x (%d)\n", cmd[0], cmdlen)); - st = -1; - continue; - } - - /* audio status? */ - if (stuffp->audiostatus == CDROM_AUDIO_INVALID) - stuffp->audiostatus = - e_audiobusy(st) ? CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS; - - /* media change? */ - if (e_changed(st)) { - INFO(("talk() media changed\n")); - stuffp->changed = jiffies; - } - - /* now actually get the data */ - while (sz--) { - if (-1 == mcdx_getval(stuffp, timeout, -1, bp++)) { - WARN(("talk() %02x timed out (data), %d tr%s left\n", - cmd[0], tries - 1, tries == 2 ? "y" : "ies")); - st = -1; break; - } - TRACE((TALK, "talk() got 0x%02x\n", *(bp - 1))); - } - } - -#if QUIET == 0 - if (!tries && st == -1) INFO(("talk() giving up\n")); -#endif - - stuffp->lock = 0; - wake_up_interruptible(&stuffp->lockq); - - TRACE((TALK, "talk() done with 0x%02x\n", st)); - return st; -} - -/* MODULE STUFF ***********************************************************/ -#ifdef MODULE - -int init_module(void) -{ - int i; - int drives = 0; - - mcdx_init(); - for (i = 0; i < MCDX_NDRIVES; i++) { - if (mcdx_stuffp[i]) { - TRACE((INIT, "init_module() drive %d stuff @ %p\n", - i, mcdx_stuffp[i])); - drives++; - } - } - - if (!drives) - return -EIO; - - return 0; -} - -void cleanup_module(void) -{ - int i; - - WARN(("cleanup_module called\n")); - - for (i = 0; i < MCDX_NDRIVES; i++) { - struct s_drive_stuff *stuffp; - stuffp = mcdx_stuffp[i]; - if (!stuffp) continue; - release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE); - free_irq(stuffp->irq); - if (stuffp->toc) { - TRACE((MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc)); - kfree(stuffp->toc); - } - TRACE((MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp)); - mcdx_stuffp[i] = NULL; - kfree(stuffp); - } - - if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) - WARN(("cleanup() unregister_blkdev() failed\n")); - else INFO(("cleanup() succeeded\n")); -} - -#endif MODULE - -/* Support functions ************************************************/ - -#if MCDX_DEBUG -void trace(int level, const char* fmt, ...) -{ - char s[255]; - va_list args; - if (level < 1) return; - va_start(args, fmt); - if (sizeof(s) < vsprintf(s, fmt, args)) - printk(MCDX ":: dprintf exeeds limit!!\n"); - else printk(MCDX ":: %s", s); - va_end(args); -} -#endif - -void warn(const char* fmt, ...) -{ - char s[255]; - va_list args; - va_start(args, fmt); - if (sizeof(s) < vsprintf(s, fmt, args)) - printk(MCDX ":: dprintf exeeds limit!!\n"); - else printk(MCDX ": %s", s); - va_end(args); -} - - -int mcdx_init(void) -{ - int drive; - - INFO((": Version 1.0a " - "mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp\n")); - - /* zero the pointer array */ - for (drive = 0; drive < MCDX_NDRIVES; drive++) - mcdx_stuffp[drive] = NULL; - - /* do the initialisation */ - for (drive = 0; drive < MCDX_NDRIVES; drive++) { - struct s_version version; - struct s_drive_stuff* stuffp; - int size; - - size = sizeof(*stuffp); - - TRACE((INIT, "init() try drive %d\n", drive)); - - TRACE((MALLOC, "init() malloc %d bytes\n", size)); - if (!(stuffp = kmalloc(size, GFP_KERNEL))) { - WARN(("init() malloc failed\n")); - break; - } - - TRACE((INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp)); - - /* zero the stuff */ - memset(stuffp, 0, sizeof(*stuffp)); - - stuffp->present = 0; /* this should be 0 already */ - stuffp->toc = NULL; /* this should be NULL already */ - stuffp->changed = jiffies; - - /* setup our irq and i/o addresses */ - stuffp->irq = irq(mcdx_drive_map[drive]); - stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]); - stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1; - stuffp->wreg_hcon = stuffp->wreg_reset + 1; - stuffp->wreg_chn = stuffp->wreg_hcon + 1; - - /* check if i/o addresses are available */ - if (0 != check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) { - WARN(("%s=0x%3p,%d: " - "Init failed. I/O ports (0x%3p..0x3p) already in use.\n" - MCDX, - stuffp->wreg_data, stuffp->irq, - stuffp->wreg_data, - stuffp->wreg_data + MCDX_IO_SIZE - 1)); - TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp)); - kfree(stuffp); - TRACE((INIT, "init() continue at next drive\n")); - continue; /* next drive */ - } - - TRACE((INIT, "init() i/o port is available at 0x%3p\n", stuffp->wreg_data)); - - TRACE((INIT, "init() hardware reset\n")); - mcdx_reset(stuffp, HARD, 1); - - TRACE((INIT, "init() get version\n")); - if (-1 == mcdx_requestversion(stuffp, &version, 4)) { - /* failed, next drive */ - WARN(("%s=0x%3p,%d: Init failed. Can't get version.\n", - MCDX, - stuffp->wreg_data, stuffp->irq)); - TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp)); - kfree(stuffp); - TRACE((INIT, "init() continue at next drive\n")); - continue; - } - - switch (version.code) { - case 'D': - stuffp->readcmd = READDSPEED; - stuffp->present = DOUBLE | DOOR | MULTI; - break; - case 'F': - stuffp->readcmd = READSSPEED; - stuffp->present = SINGLE | DOOR | MULTI; - break; - case 'M': - stuffp->readcmd = READSSPEED; - stuffp->present = SINGLE; - break; - default: - stuffp->present = 0; break; - } - - stuffp->playcmd = READSSPEED; - - - if (!stuffp->present) { - WARN(("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n", - MCDX, stuffp->wreg_data, stuffp->irq)); - kfree(stuffp); - continue; /* next drive */ - } - - TRACE((INIT, "init() register blkdev\n")); - if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) { - WARN(("%s=0x%3p,%d: Init failed. Can't get major %d.\n", - MCDX, - stuffp->wreg_data, stuffp->irq, MAJOR_NR)); - kfree(stuffp); - continue; /* next drive */ - } - - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; - read_ahead[MAJOR_NR] = READ_AHEAD; - -#if WE_KNOW_WHY - blksize_size[MAJOR_NR] = BLKSIZES; -#endif - - TRACE((INIT, "init() subscribe irq and i/o\n")); - mcdx_irq_map[stuffp->irq] = stuffp; - if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, DEVICE_NAME)) { - WARN(("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n", - MCDX, - stuffp->wreg_data, stuffp->irq, stuffp->irq)); - stuffp->irq = 0; - kfree(stuffp); - continue; - } - request_region((unsigned int) stuffp->wreg_data, - MCDX_IO_SIZE, - DEVICE_NAME); - - TRACE((INIT, "init() get garbage\n")); - { - int i; - mcdx_delay(stuffp, HZ/2); - for (i = 100; i; i--) (void) inb((unsigned int) stuffp->rreg_status); - } - - -#if WE_KNOW_WHY - outb(0x50, (unsigned int) stuffp->wreg_chn); /* irq 11 -> channel register */ -#endif - - TRACE((INIT, "init() set non dma but irq mode\n")); - mcdx_config(stuffp, 1); - - stuffp->minor = drive; - - WARN((DEVICE_NAME " installed at 0x%3p, irq %d." - " (Firmware version %c %x)\n", - stuffp->wreg_data, stuffp->irq, version.code, - version.ver)); - mcdx_stuffp[drive] = stuffp; - TRACE((INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp)); - } - - return 0; -} - - -static int mcdx_transfer(struct s_drive_stuff *stuffp, - char *p, int sector, int nr_sectors) -/* This does actually the transfer from the drive. - Return: -1 on timeout or other error - else status byte (as in stuff->st) */ -{ - int off; - int done = 0; - - TRACE((TRANSFER, "transfer() %d sectors at sector %d\n", - nr_sectors, sector)); - - if (stuffp->audio) { - WARN(("attempt to read from audio cd\n")); - return -1; - } - - while (stuffp->lock) - interruptible_sleep_on(&stuffp->lockq); - if (current->signal && ~current->blocked) { - WARN(("talk() got signal %d\n", current->signal)); - } - - if (stuffp->valid - && (sector >= stuffp->pending) - && (sector < stuffp->off_direct)) { - - - off = stuffp->off_requested < (off = sector + nr_sectors) - ? stuffp->off_requested : off; - - stuffp->lock = current->pid; - - do { - int sig = 0; - int to = 0; - - /* wait for the drive become idle */ - current->timeout = jiffies + 5*HZ; - while (stuffp->busy) { - interruptible_sleep_on(&stuffp->busyq); - if ((sig = (current->signal && ~current->blocked)) - || (to = (current->timeout == 0))) { - break; - } - } - - current->timeout = 0; - - /* test for possible errors */ - if (((stuffp->busy == 0) && !stuffp->introk) - || sig - || to) { - if ((stuffp->busy == 0) && !stuffp->introk) - WARN(("mcdx_transfer() failure in data request\n")); - else if (to) - WARN(("mcdx_transfer(): timeout\n")); - else if (sig) - WARN(("mcdx_transfer(): got signal 0x%lx\n", current->signal)); - - stuffp->lock = 0; - stuffp->busy = 0; - wake_up_interruptible(&stuffp->lockq); - wake_up_interruptible(&stuffp->busyq); - stuffp->errno = MCDX_E; - TRACE((TRANSFER, "transfer() done (-1)\n")); - return -1; - } - - /* test if it's the first sector of a block, - * there we have to skip some bytes as we read raw data */ - if (stuffp->xa && (0 == (stuffp->pending & 3))) { - const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE; - TRACE((TRANSFER, "transfer() sector %d, skip %d header bytes\n", - stuffp->pending, HEAD)); - insb((unsigned int) stuffp->rreg_data, p, HEAD); - } - - /* now actually read the data */ - - TRACE((TRANSFER, "transfer() read sector %d\n", stuffp->pending)); - insb((unsigned int) stuffp->rreg_data, p, 512); - - /* test if it's the last sector of a block, - * if so, we have to expect an interrupt and to skip some - * data too */ - if ((stuffp->busy = (3 == (stuffp->pending & 3))) && stuffp->xa) { - char dummy[CD_XA_TAIL]; - TRACE((TRANSFER, "transfer() sector %d, skip %d trailer bytes\n", - stuffp->pending, CD_XA_TAIL)); - insb((unsigned int) stuffp->rreg_data, &dummy[0], CD_XA_TAIL); - } - - if (stuffp->pending == sector) { - p += 512; - done++; - sector++; - } - } - while (++(stuffp->pending) < off); - - stuffp->lock = 0; - wake_up_interruptible(&stuffp->lockq); - - } else { - - static unsigned char cmd[] = { - 0, - 0, 0, 0, - 0, 0, 0 - }; - - cmd[0] = stuffp->readcmd; - - stuffp->valid = 1; - stuffp->pending = sector & ~3; - - /* do some sanity checks */ - TRACE((TRANSFER, "transfer() request sector %d\n", stuffp->pending)); - if (stuffp->pending > stuffp->lastsector) { - WARN(("transfer() sector %d from nirvana requested.\n", - stuffp->pending)); - stuffp->errno = MCDX_EOM; - TRACE((TRANSFER, "transfer() done (-1)\n")); - return -1; - } - - if ((stuffp->off_direct = stuffp->pending + DIRECT_SIZE) - > stuffp->lastsector + 1) - stuffp->off_direct = stuffp->lastsector + 1; - if ((stuffp->off_requested = stuffp->pending + REQUEST_SIZE) - > stuffp->lastsector + 1) - stuffp->off_requested = stuffp->lastsector + 1; - - TRACE((TRANSFER, "transfer() pending %d\n", stuffp->pending)); - TRACE((TRANSFER, "transfer() off_dir %d\n", stuffp->off_direct)); - TRACE((TRANSFER, "transfer() off_req %d\n", stuffp->off_requested)); - - { - struct s_msf pending; - log2msf(stuffp->pending / 4, &pending); - cmd[1] = pending.minute; - cmd[2] = pending.second; - cmd[3] = pending.frame; - } - - stuffp->busy = 1; - cmd[6] = (unsigned char) (stuffp->off_requested - stuffp->pending) / 4; - - outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd); - - } - - stuffp->off_direct = (stuffp->off_direct += done) < stuffp->off_requested - ? stuffp->off_direct : stuffp->off_requested; - - TRACE((TRANSFER, "transfer() done (%d)\n", done)); - return done; -} - - -/* Access to elements of the mcdx_drive_map members */ - -static char* port(int *ip) { return (char*) ip[0]; } -static int irq(int *ip) { return ip[1]; } - -/* Misc number converters */ - -static unsigned int bcd2uint(unsigned char c) -{ return (c >> 4) * 10 + (c & 0x0f); } - -static unsigned int uint2bcd(unsigned int ival) -{ return ((ival / 10) << 4) | (ival % 10); } - -static void log2msf(unsigned int l, struct s_msf* pmsf) -{ - l += CD_BLOCK_OFFSET; - pmsf->minute = uint2bcd(l / 4500), l %= 4500; - pmsf->second = uint2bcd(l / 75); - pmsf->frame = uint2bcd(l % 75); -} - -static unsigned int msf2log(const struct s_msf* pmsf) -{ - return bcd2uint(pmsf->frame) - + bcd2uint(pmsf->second) * 75 - + bcd2uint(pmsf->minute) * 4500 - - CD_BLOCK_OFFSET; -} - -int mcdx_readtoc(struct s_drive_stuff* stuffp) -/* Read the toc entries from the CD, - * Return: -1 on failure, else 0 */ -{ - - if (stuffp->toc) { - TRACE((IOCTL, "ioctl() toc already read\n")); - return 0; - } - - TRACE((IOCTL, "ioctl() readtoc for %d tracks\n", - stuffp->di.n_last - stuffp->di.n_first + 1)); - - if (-1 == mcdx_hold(stuffp, 1)) return -1; - - TRACE((IOCTL, "ioctl() tocmode\n")); - if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO; - - /* all seems to be ok so far ... malloc */ - { - int size; - size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2); - - TRACE((MALLOC, "ioctl() malloc %d bytes\n", size)); - stuffp->toc = kmalloc(size, GFP_KERNEL); - if (!stuffp->toc) { - WARN(("Cannot malloc %s bytes for toc\n", size)); - mcdx_setdrivemode(stuffp, DATA, 1); - return -EIO; - } - } - - /* now read actually the index */ - { - int trk; - int retries; - - for (trk = 0; - trk < (stuffp->di.n_last - stuffp->di.n_first + 1); - trk++) - stuffp->toc[trk].index = 0; - - for (retries = 300; retries; retries--) { /* why 300? */ - struct s_subqcode q; - unsigned int idx; - - if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) { - mcdx_setdrivemode(stuffp, DATA, 1); - return -EIO; - } - - idx = bcd2uint(q.index); - - if ((idx > 0) - && (idx <= stuffp->di.n_last) - && (q.tno == 0) - && (stuffp->toc[idx - stuffp->di.n_first].index == 0)) { - stuffp->toc[idx - stuffp->di.n_first] = q; - TRACE((IOCTL, "ioctl() toc idx %d (trk %d)\n", idx, trk)); - trk--; - } - if (trk == 0) break; - } - memset(&stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1], - 0, sizeof(stuffp->toc[0])); - stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt - = stuffp->di.msf_leadout; - } - - /* unset toc mode */ - TRACE((IOCTL, "ioctl() undo toc mode\n")); - if (-1 == mcdx_setdrivemode(stuffp, DATA, 2)) - return -EIO; - -#if MCDX_DEBUG && IOCTL - { int trk; - for (trk = 0; - trk < (stuffp->di.n_last - stuffp->di.n_first + 2); - trk++) - TRACE((IOCTL, "ioctl() %d readtoc %02x %02x %02x" - " %02x:%02x.%02x %02x:%02x.%02x\n", - trk + stuffp->di.n_first, - stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index, - stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame, - stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame)); - } -#endif - - return 0; -} - -static int -mcdx_playmsf(struct s_drive_stuff* stuffp, const struct cdrom_msf* msf) -{ - unsigned char cmd[7] = { - 0, 0, 0, 0, 0, 0, 0 - }; - - cmd[0] = stuffp->playcmd; - - cmd[1] = msf->cdmsf_min0; - cmd[2] = msf->cdmsf_sec0; - cmd[3] = msf->cdmsf_frame0; - cmd[4] = msf->cdmsf_min1; - cmd[5] = msf->cdmsf_sec1; - cmd[6] = msf->cdmsf_frame1; - - TRACE((IOCTL, "ioctl(): play %x " - "%02x:%02x:%02x -- %02x:%02x:%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3], - cmd[4], cmd[5], cmd[6])); - - outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd); - - if (-1 == mcdx_getval(stuffp, 1*HZ, 0, NULL)) { - WARN(("playmsf() timeout\n")); - return -1; - } - - stuffp->audiostatus = CDROM_AUDIO_PLAY; - return 0; -} - -static int -mcdx_playtrk(struct s_drive_stuff* stuffp, const struct cdrom_ti* ti) -{ - struct s_subqcode* p; - struct cdrom_msf msf; - - if (-1 == mcdx_readtoc(stuffp)) return -1; - - if (ti) p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first]; - else p = &stuffp->start; - - msf.cdmsf_min0 = p->dt.minute; - msf.cdmsf_sec0 = p->dt.second; - msf.cdmsf_frame0 = p->dt.frame; - - if (ti) { - p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1]; - stuffp->stop = *p; - } else p = &stuffp->stop; - - msf.cdmsf_min1 = p->dt.minute; - msf.cdmsf_sec1 = p->dt.second; - msf.cdmsf_frame1 = p->dt.frame; - - return mcdx_playmsf(stuffp, &msf); -} - - -/* Drive functions ************************************************/ - -static int -mcdx_closedoor(struct s_drive_stuff *stuffp, int tries) -{ - if (stuffp->present & DOOR) - return mcdx_talk(stuffp, "\xf8", 1, NULL, 0, 5*HZ, tries); - else - return 0; -} - -static int -mcdx_stop(struct s_drive_stuff *stuffp, int tries) -{ return mcdx_talk(stuffp, "\xf0", 1, NULL, 0, 2*HZ, tries); } - -static int -mcdx_hold(struct s_drive_stuff *stuffp, int tries) -{ return mcdx_talk(stuffp, "\x70", 1, NULL, 0, 2*HZ, tries); } - -static int -mcdx_eject(struct s_drive_stuff *stuffp, int tries) -{ - if (stuffp->present & DOOR) - return mcdx_talk(stuffp, "\xf6", 1, NULL, 0, 5*HZ, tries); - else - return 0; -} - -static int -mcdx_requestsubqcode(struct s_drive_stuff *stuffp, - struct s_subqcode *sub, - int tries) -{ - char buf[11]; - int ans; - - if (-1 == (ans = mcdx_talk( - stuffp, "\x20", 1, buf, sizeof(buf), - 2*HZ, tries))) - return -1; - sub->control = buf[1]; - sub->tno = buf[2]; - sub->index = buf[3]; - sub->tt.minute = buf[4]; - sub->tt.second = buf[5]; - sub->tt.frame = buf[6]; - sub->dt.minute = buf[8]; - sub->dt.second = buf[9]; - sub->dt.frame = buf[10]; - - return ans; -} - -static int -mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, struct s_multi *multi, int tries) -{ - char buf[5]; - int ans; - - if (stuffp->present & MULTI) { - ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2*HZ, tries); - multi->multi = buf[1]; - multi->msf_last.minute = buf[2]; - multi->msf_last.second = buf[3]; - multi->msf_last.frame = buf[4]; - return ans; - } else { - multi->multi = 0; - return 0; - } -} - -static int -mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, int tries) -{ - char buf[9]; - int ans; - ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2*HZ, tries); - info->n_first = bcd2uint(buf[1]); - info->n_last = bcd2uint(buf[2]); - info->msf_leadout.minute = buf[3]; - info->msf_leadout.second = buf[4]; - info->msf_leadout.frame = buf[5]; - info->msf_first.minute = buf[6]; - info->msf_first.second = buf[7]; - info->msf_first.frame = buf[8]; - return ans; -} - -static int -mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries) -{ - char cmd[2]; - int ans; - - TRACE((HW, "setdrivemode() %d\n", mode)); - - if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5*HZ, tries))) - return -1; - - switch (mode) { - case TOC: cmd[1] |= 0x04; break; - case DATA: cmd[1] &= ~0x04; break; - case RAW: cmd[1] |= 0x40; break; - case COOKED: cmd[1] &= ~0x40; break; - default: break; - } - cmd[0] = 0x50; - return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries); -} - - -static int -mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries) -{ - unsigned char cmd[2] = { 0xa0 }; - TRACE((HW, "setdatamode() %d\n", mode)); - switch (mode) { - case MODE0: cmd[1] = 0x00; break; - case MODE1: cmd[1] = 0x01; break; - case MODE2: cmd[1] = 0x02; break; - default: return -EINVAL; - } - return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries); -} - -static int -mcdx_config(struct s_drive_stuff *stuffp, int tries) -{ - char cmd[4]; - - TRACE((HW, "config()\n")); - - cmd[0] = 0x90; - - cmd[1] = 0x10; /* irq enable */ - cmd[2] = 0x05; /* pre, err irq enable */ - - if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries)) - return -1; - - cmd[1] = 0x02; /* dma select */ - cmd[2] = 0x00; /* no dma */ - - return mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries); -} - -static int -mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, int tries) -{ - char buf[3]; - int ans; - - if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 1, buf, sizeof(buf), 2*HZ, tries))) - return ans; - - ver->code = buf[1]; - ver->ver = buf[2]; - - return ans; -} - -static int -mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries) -{ - if (mode == HARD) { - outb(0, (unsigned int) stuffp->wreg_chn); /* no dma, no irq -> hardware */ - outb(0, (unsigned int) stuffp->wreg_reset); /* hw reset */ - return 0; - } else return mcdx_talk(stuffp, "\x60", 1, NULL, 0, 5*HZ, tries); -} - -static int -mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries) -{ - char cmd[2] = { 0xfe }; - if (stuffp->present & DOOR) { - cmd[1] = lock ? 0x01 : 0x00; - return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 0, 5*HZ, tries); - } else - return 0; -} - -static int -mcdx_getstatus(struct s_drive_stuff *stuffp, int tries) -{ return mcdx_talk(stuffp, "\x40", 1, NULL, 0, 5*HZ, tries); } - -static int -mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char* buf) -{ - unsigned long timeout = to + jiffies; - char c; - - if (!buf) buf = &c; - - while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) { - if (jiffies > timeout) return -1; - mcdx_delay(stuffp, delay); - } - - *buf = (unsigned char) inb((unsigned int) stuffp->rreg_data) & 0xff; - - return 0; -} diff -u --recursive --new-file v1.3.35/linux/drivers/block/optcd.c linux/drivers/block/optcd.c --- v1.3.35/linux/drivers/block/optcd.c Tue Oct 10 18:46:32 1995 +++ linux/drivers/block/optcd.c Thu Jan 1 02:00:00 1970 @@ -1,1669 +0,0 @@ -/* $Id: optcd.c,v 1.3 1995/08/24 19:54:27 root Exp root $ - linux/drivers/block/optcd.c - Optics Storage 8000 AT CDROM driver - - Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl) - - Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks - by Eberhard Moenkeberg (emoenke@gwdg.de). ISP16 detection and - configuration by Eric van der Maarel (maarel@marin.nl), with some data - communicated by Vadim V. Model (vadim@rbrf.msk.su). - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - History - 14-5-95 v0.0 Plays sound tracks. No reading of data CDs yet. - Detection of disk change doesn't work. - 21-5-95 v0.1 First ALPHA version. CD can be mounted. The - device major nr is borrowed from the Aztech - driver. Speed is around 240 kb/s, as measured - with "time dd if=/dev/cdrom of=/dev/null \ - bs=2048 count=4096". - 24-6-95 v0.2 Reworked the #defines for the command codes - and the like, as well as the structure of - the hardware communication protocol, to - reflect the "official" documentation, kindly - supplied by C.K. Tan, Optics Storage Pte. Ltd. - Also tidied up the state machine somewhat. - 28-6-95 v0.3 Removed the ISP-16 interface code, as this - should go into its own driver. The driver now - has its own major nr. - Disk change detection now seems to work, too. - This version became part of the standard - kernel as of version 1.3.7 - 24-9-95 v0.4 Re-inserted ISP-16 interface code which I - copied from sjcd.c, with a few changes. - Updated README.optcd. Submitted for - inclusion in 1.3.21 - 29-9-95 v0.4a Fixed bug that prevented compilation as module -*/ - -#include -#include - -#ifdef MODULE -# include -# include -# 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 -#include -#include -#include -#include -#include -#include -#include - -#define MAJOR_NR OPTICS_CDROM_MAJOR - -# include "blk.h" -#define optcd_port optcd /* Needed for the modutils. */ -# include - - -/* 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 - * - * Version 0.5 - * - * Detect cdrom interface on ISP16 soundcard. - * Configure cdrom interface. - * - * Algorithm for the card with no IDE support option taken - * from the CDSETUP.SYS driver for MSDOS, - * by OPTi Computers, version 2.03. - * Algorithm for the IDE supporting ISP16 as communicated - * to me by Vadim Model and Leo Spiekman. - * - * Use, modifification or redistribution of this software is - * allowed under the terms of the GPL. - * - */ - - -#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p)) -#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p)) - -static short -isp16_detect(void) -{ - - if ( !( isp16_with_ide__detect() < 0 ) ) - return(2); - else - return( isp16_no_ide__detect() ); -} - -static short -isp16_no_ide__detect(void) -{ - u_char ctrl; - u_char enable_cdrom; - u_char io; - short i = -1; - - isp16_ctrl = ISP16_NO_IDE__CTRL; - isp16_enable_cdrom_port = ISP16_NO_IDE__ENABLE_CDROM_PORT; - - /* read' and write' are a special read and write, respectively */ - - /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */ - ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC; - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - - /* read' 3,4 and 5-bit from the cdrom enable port */ - enable_cdrom = ISP16_IN( ISP16_NO_IDE__ENABLE_CDROM_PORT ) & 0x38; - - if ( !(enable_cdrom & 0x20) ) { /* 5-bit not set */ - /* read' last 2 bits of ISP16_IO_SET_PORT */ - io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03; - if ( ((io&0x01)<<1) == (io&0x02) ) { /* bits are the same */ - if ( io == 0 ) { /* ...the same and 0 */ - i = 0; - enable_cdrom |= 0x20; - } - else { /* ...the same and 1 */ /* my card, first time 'round */ - i = 1; - enable_cdrom |= 0x28; - } - ISP16_OUT( ISP16_NO_IDE__ENABLE_CDROM_PORT, enable_cdrom ); - } - else { /* bits are not the same */ - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - return(i); /* -> not detected: possibly incorrect conclusion */ - } - } - else if ( enable_cdrom == 0x20 ) - i = 0; - else if ( enable_cdrom == 0x28 ) /* my card, already initialised */ - i = 1; - - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - - return(i); -} - -static short -isp16_with_ide__detect(void) -{ - u_char ctrl; - u_char tmp; - - isp16_ctrl = ISP16_IDE__CTRL; - isp16_enable_cdrom_port = ISP16_IDE__ENABLE_CDROM_PORT; - - /* read' and write' are a special read and write, respectively */ - - /* read' ISP16_CTRL_PORT and save */ - ctrl = ISP16_IN( ISP16_CTRL_PORT ); - - /* write' zero to the ctrl port and get response */ - ISP16_OUT( ISP16_CTRL_PORT, 0 ); - tmp = ISP16_IN( ISP16_CTRL_PORT ); - - if ( tmp != 2 ) /* isp16 with ide option not detected */ - return(-1); - - /* restore ctrl port value */ - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - - return(2); -} - -static short -isp16_config( int base, u_char drive_type, int irq, int dma ) -{ - u_char base_code; - u_char irq_code; - u_char dma_code; - u_char i; - - if ( (drive_type == ISP16_MITSUMI) && (dma != 0) ) - printk( "Mitsumi cdrom drive has no dma support.\n" ); - - switch (base) { - case 0x340: base_code = ISP16_BASE_340; break; - case 0x330: base_code = ISP16_BASE_330; break; - case 0x360: base_code = ISP16_BASE_360; break; - case 0x320: base_code = ISP16_BASE_320; break; - default: - printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base ); - return(-1); - } - switch (irq) { - case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */ - case 5: irq_code = ISP16_IRQ_5; - printk( "Irq 5 shouldn't be used by cdrom interface on ISP16," - " due to possible conflicts with the soundcard.\n"); - break; - case 7: irq_code = ISP16_IRQ_7; - printk( "Irq 7 shouldn't be used by cdrom interface on ISP16," - " due to possible conflicts with the soundcard.\n"); - break; - case 3: irq_code = ISP16_IRQ_3; break; - case 9: irq_code = ISP16_IRQ_9; break; - case 10: irq_code = ISP16_IRQ_10; break; - case 11: irq_code = ISP16_IRQ_11; break; - default: - printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq ); - return(-1); - } - switch (dma) { - case 0: dma_code = ISP16_DMA_X; break; /* disable dma */ - case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16," - " due to conflict with the soundcard.\n"); - return(-1); break; - case 3: dma_code = ISP16_DMA_3; break; - case 5: dma_code = ISP16_DMA_5; break; - case 6: dma_code = ISP16_DMA_6; break; - case 7: dma_code = ISP16_DMA_7; break; - default: - printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma ); - return(-1); - } - - if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 && - drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 && - drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI && - drive_type != ISP16_DRIVE_X ) { - printk( "Drive type (code 0x%02X) not supported by cdrom" - " interface on ISP16.\n", drive_type ); - return(-1); - } - - /* set type of interface */ - i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK; /* clear some bits */ - ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type ); - - /* enable cdrom on interface with ide support */ - if ( isp16_type > 1 ) - ISP16_OUT( isp16_enable_cdrom_port, ISP16_ENABLE_CDROM ); - - /* set base address, irq and dma */ - i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK; /* keep some bits */ - ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code ); - - return(0); -} diff -u --recursive --new-file v1.3.35/linux/drivers/block/ramdisk.c linux/drivers/block/ramdisk.c --- v1.3.35/linux/drivers/block/ramdisk.c Mon Sep 18 14:53:50 1995 +++ linux/drivers/block/ramdisk.c Sat Oct 21 19:37:35 1995 @@ -20,7 +20,7 @@ #include #define MAJOR_NR MEM_MAJOR -#include "blk.h" +#include #define RAMDISK_MINOR 1 diff -u --recursive --new-file v1.3.35/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c --- v1.3.35/linux/drivers/block/sbpcd.c Tue Oct 10 18:46:32 1995 +++ linux/drivers/block/sbpcd.c Thu Jan 1 02:00:00 1970 @@ -1,5422 +0,0 @@ -/* - * sbpcd.c CD-ROM device driver for the whole family of IDE-style - * Kotobuki/Matsushita/Panasonic CR-5xx drives for - * SoundBlaster ("Pro" or "16 ASP" or compatible) cards - * and for "no-sound" interfaces like Lasermate and the - * Panasonic CI-101P. - * Also for the Longshine LCS-7260 drive. - * Also for the IBM "External ISA CD-Rom" drive. - * Also for the CreativeLabs CD200 drive (but I still need some - * detailed bug reports). - * Also for the TEAC CD-55A drive. - * Not for Sanyo drives (but sjcd is there...). - * Not for any other Funai drives than E2550UA (="CD200" with "F"). - * - * NOTE: This is release 3.9. - * - * VERSION HISTORY - * - * 0.1 initial release, April/May 93, after mcd.c (Martin Harriss) - * - * 0.2 the "repeat:"-loop in do_sbpcd_request did not check for - * end-of-request_queue (resulting in kernel panic). - * Flow control seems stable, but throughput is not better. - * - * 0.3 interrupt locking totally eliminated (maybe "inb" and "outb" - * are still locking) - 0.2 made keyboard-type-ahead losses. - * check_sbpcd_media_change added (to use by isofs/inode.c) - * - but it detects almost nothing. - * - * 0.4 use MAJOR 25 definitely. - * Almost total re-design to support double-speed drives and - * "naked" (no sound) interface cards ("LaserMate" interface type). - * Flow control should be exact now. - * Don't occupy the SbPro IRQ line (not needed either); will - * live together with Hannu Savolainen's sndkit now. - * Speeded up data transfer to 150 kB/sec, with help from Kai - * Makisara, the "provider" of the "mt" tape utility. - * Give "SpinUp" command if necessary. - * First steps to support up to 4 drives (but currently only one). - * Implemented audio capabilities - workman should work, xcdplayer - * gives some problems. - * This version is still consuming too much CPU time, and - * sleeping still has to be worked on. - * During "long" implied seeks, it seems possible that a - * ReadStatus command gets ignored. That gives the message - * "ResponseStatus timed out" (happens about 6 times here during - * a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is - * handled without data error, but it should get done better. - * - * 0.5 Free CPU during waits (again with help from Kai Makisara). - * Made it work together with the LILO/kernel setup standard. - * Included auto-probing code, as suggested by YGGDRASIL. - * Formal redesign to add DDI debugging. - * There are still flaws in IOCTL (workman with double speed drive). - * - * 1.0 Added support for all drive IDs (0...3, no longer only 0) - * and up to 4 drives on one controller. - * Added "#define MANY_SESSION" for "old" multi session CDs. - * - * 1.1 Do SpinUp for new drives, too. - * Revised for clean compile under "old" kernels (0.99pl9). - * - * 1.2 Found the "workman with double-speed drive" bug: use the driver's - * audio_state, not what the drive is reporting with ReadSubQ. - * - * 1.3 Minor cleanups. - * Refinements regarding Workman. - * - * 1.4 Read XA disks (PhotoCDs) with "old" drives, too (but only the first - * session - no chance to fully access a "multi-session" CD). - * This currently still is too slow (50 kB/sec) - but possibly - * the old drives won't do it faster. - * Implemented "door (un)lock" for new drives (still does not work - * as wanted - no lock possible after an unlock). - * Added some debugging printout for the UPC/EAN code - but my drives - * return only zeroes. Is there no UPC/EAN code written? - * - * 1.5 Laborate with UPC/EAN code (not better yet). - * Adapt to kernel 1.1.8 change (have to explicitly include - * 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 - * - * 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 - -#ifdef MODULE -#include -#include -#ifndef CONFIG_MODVERSIONS -char kernel_version[]=UTS_RELEASE; -#endif -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif MODULE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 " - -/*==========================================================================*/ -/* - * 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<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<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;i0) msg(DBG_INF,"ResponseInfo: got %d trailing bytes.\n",j); -#endif 000 - for (j=0;j0) 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)||(timeout0;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;j1) 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_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>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_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>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;i200) - { - 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;j0;i--) OUT(port+0,0); - for (k=0;k0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i0;i--) OUT(port+0,0); - for (k=0;k0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i0;i--) OUT(port+0,0); - for (k=0;k0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i0;i--) OUT(port+0,0); - for (k=0;k0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i>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=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_trk0D_S[d].n_last_track) return (-EINVAL); - if (ti.cdti_trk1D_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 (iD_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>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= 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;j1) 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=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=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;j0) 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;j0) - vfree(D_S[j].aud_buf); - } - msg(DBG_INF, "%s module released.\n", major_name); -} -#endif MODULE -/*==========================================================================*/ -/* - * Check if the media has changed in the CD-ROM drive. - * used externally (isofs/inode.c, fs/buffer.c) - * Currently disabled (has to get "synchronized"). - */ -static int sbpcd_chk_disk_change(kdev_t full_dev) -{ - int i, st; - - msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev)); - return (0); /* "busy" test necessary before we really can check */ - - i=MINOR(full_dev); - if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) ) - { - msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev); - return (-1); - } - - switch_drive(i); - - cc_ReadStatus(); /* command: give 1-byte status */ - st=ResponseStatus(); - msg(DBG_CHK,"media_check: %02X\n",D_S[d].status_bits); - if (st<0) - { - msg(DBG_INF,"media_check: ResponseStatus error.\n"); - return (1); /* status not obtainable */ - } - if (D_S[d].CD_changed==0xFF) msg(DBG_CHK,"media_check: \"changed\" assumed.\n"); - if (!st_spinning) msg(DBG_CHK,"media_check: motor off.\n"); - if (!st_door_closed) - { - msg(DBG_CHK,"media_check: door open.\n"); - D_S[d].CD_changed=0xFF; - } - if (!st_caddy_in) - { - msg(DBG_CHK,"media_check: no disk in drive.\n"); - D_S[d].open_count=0; - D_S[d].CD_changed=0xFF; - } - if (!st_diskok) msg(DBG_CHK,"media_check: !st_diskok.\n"); - -#if 0000 - if (D_S[d].CD_changed==0xFF) - { - D_S[d].CD_changed=1; - return (1); /* driver had a change detected before */ - } -#endif 0000 /* seems to give additional errors at the moment */ - - if (!st_diskok) return (1); /* disk not o.k. */ - if (!st_caddy_in) return (1); /* disk removed */ - if (!st_door_closed) return (1); /* door open */ - return (0); -} -/*==========================================================================*/ -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 8 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -8 - * c-argdecl-indent: 8 - * c-label-offset: -8 - * c-continued-statement-offset: 8 - * c-continued-brace-offset: 0 - * End: - */ diff -u --recursive --new-file v1.3.35/linux/drivers/block/sbpcd2.c linux/drivers/block/sbpcd2.c --- v1.3.35/linux/drivers/block/sbpcd2.c Fri Jan 13 09:02:32 1995 +++ linux/drivers/block/sbpcd2.c Thu Jan 1 02:00:00 1970 @@ -1,5 +0,0 @@ -/* - * duplication of sbpcd.c for multiple interfaces - */ -#define SBPCD_ISSUE 2 -#include "sbpcd.c" diff -u --recursive --new-file v1.3.35/linux/drivers/block/sbpcd3.c linux/drivers/block/sbpcd3.c --- v1.3.35/linux/drivers/block/sbpcd3.c Fri Jan 13 09:02:32 1995 +++ linux/drivers/block/sbpcd3.c Thu Jan 1 02:00:00 1970 @@ -1,5 +0,0 @@ -/* - * duplication of sbpcd.c for multiple interfaces - */ -#define SBPCD_ISSUE 3 -#include "sbpcd.c" diff -u --recursive --new-file v1.3.35/linux/drivers/block/sbpcd4.c linux/drivers/block/sbpcd4.c --- v1.3.35/linux/drivers/block/sbpcd4.c Fri Jan 13 09:02:32 1995 +++ linux/drivers/block/sbpcd4.c Thu Jan 1 02:00:00 1970 @@ -1,5 +0,0 @@ -/* - * duplication of sbpcd.c for multiple interfaces - */ -#define SBPCD_ISSUE 4 -#include "sbpcd.c" diff -u --recursive --new-file v1.3.35/linux/drivers/block/sjcd.c linux/drivers/block/sjcd.c --- v1.3.35/linux/drivers/block/sjcd.c Mon Oct 16 18:38:22 1995 +++ linux/drivers/block/sjcd.c Thu Jan 1 02:00:00 1970 @@ -1,1915 +0,0 @@ -/* -- sjcd.c - * - * Sanyo CD-ROM device driver implementation, Version 1.5 - * Copyright (C) 1995 Vadim V. Model - * - * model@cecmow.enet.dec.com - * vadim@rbrf.ru - * vadim@ipsun.ras.ru - * - * ISP16 detection and configuration. - * Copyright (C) 1995 Eric van der Maarel (maarel@marin.nl) - * and Vadim Model (vadim@cecmow.enet.dec.com) - * - * - * This driver is based on pre-works by Eberhard Moenkeberg (emoenke@gwdg.de); - * it was developed under use of mcd.c from Martin Harriss, with help of - * Eric van der Maarel (maarel@marin.nl). - * - * ISP16 detection and configuration by Eric van der Maarel (maarel@marin.nl). - * Sound configuration by Vadim V. Model (model@cecmow.enet.dec.com) - * - * It is planned to include these routines into sbpcd.c later - to make - * a "mixed use" on one cable possible for all kinds of drives which use - * the SoundBlaster/Panasonic style CDROM interface. But today, the - * ability to install directly from CDROM is more important than flexibility. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * History: - * 1.1 First public release with kernel version 1.3.7. - * Written by Vadim Model. - * 1.2 Added detection and configuration of cdrom interface - * on ISP16 soundcard. - * Allow for command line options: sjcd=,, - * 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 -#include - -#ifdef MODULE -#include -#include -#define sjcd_init init_module -#ifndef CONFIG_MODVERSIONS -char kernel_version[]= UTS_RELEASE; -#endif -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MAJOR_NR SANYO_CDROM_MAJOR -#include "blk.h" -#include - -/* 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 - * - * Version 0.5 - * - * Detect cdrom interface on ISP16 soundcard. - * Configure cdrom interface. - * Configure sound interface. - * - * Algorithm for the card with OPTi 82C928 taken - * from the CDSETUP.SYS driver for MSDOS, - * by OPTi Computers, version 2.03. - * Algorithm for the card with OPTi 82C929 as communicated - * to me by Vadim Model and Leo Spiekman. - * - * Use, modifification or redistribution of this software is - * allowed under the terms of the GPL. - * - */ - - -#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p)) -#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p)) - -static short -isp16_detect(void) -{ - - if ( !( isp16_c929__detect() < 0 ) ) - return(2); - else - return( isp16_c928__detect() ); -} - -static short -isp16_c928__detect(void) -{ - u_char ctrl; - u_char enable_cdrom; - u_char io; - short i = -1; - - isp16_ctrl = ISP16_C928__CTRL; - isp16_enable_port = ISP16_C928__ENABLE_PORT; - - /* read' and write' are a special read and write, respectively */ - - /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */ - ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC; - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - - /* read' 3,4 and 5-bit from the cdrom enable port */ - enable_cdrom = ISP16_IN( ISP16_C928__ENABLE_PORT ) & 0x38; - - if ( !(enable_cdrom & 0x20) ) { /* 5-bit not set */ - /* read' last 2 bits of ISP16_IO_SET_PORT */ - io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03; - if ( ((io&0x01)<<1) == (io&0x02) ) { /* bits are the same */ - if ( io == 0 ) { /* ...the same and 0 */ - i = 0; - enable_cdrom |= 0x20; - } - else { /* ...the same and 1 */ /* my card, first time 'round */ - i = 1; - enable_cdrom |= 0x28; - } - ISP16_OUT( ISP16_C928__ENABLE_PORT, enable_cdrom ); - } - else { /* bits are not the same */ - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - return(i); /* -> not detected: possibly incorrect conclusion */ - } - } - else if ( enable_cdrom == 0x20 ) - i = 0; - else if ( enable_cdrom == 0x28 ) /* my card, already initialised */ - i = 1; - - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - - return(i); -} - -static short -isp16_c929__detect(void) -{ - u_char ctrl; - u_char tmp; - - isp16_ctrl = ISP16_C929__CTRL; - isp16_enable_port = ISP16_C929__ENABLE_PORT; - - /* read' and write' are a special read and write, respectively */ - - /* read' ISP16_CTRL_PORT and save */ - ctrl = ISP16_IN( ISP16_CTRL_PORT ); - - /* write' zero to the ctrl port and get response */ - ISP16_OUT( ISP16_CTRL_PORT, 0 ); - tmp = ISP16_IN( ISP16_CTRL_PORT ); - - if ( tmp != 2 ) /* isp16 with 82C929 not detected */ - return(-1); - - /* restore ctrl port value */ - ISP16_OUT( ISP16_CTRL_PORT, ctrl ); - - return(2); -} - -static short -isp16_cdi_config( int base, u_char drive_type, int irq, int dma ) -{ - u_char base_code; - u_char irq_code; - u_char dma_code; - u_char i; - - if ( (drive_type == ISP16_MITSUMI) && (dma != 0) ) - printk( "Mitsumi cdrom drive has no dma support.\n" ); - - switch (base) { - case 0x340: base_code = ISP16_BASE_340; break; - case 0x330: base_code = ISP16_BASE_330; break; - case 0x360: base_code = ISP16_BASE_360; break; - case 0x320: base_code = ISP16_BASE_320; break; - default: - printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base ); - return(-1); - } - switch (irq) { - case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */ - case 5: irq_code = ISP16_IRQ_5; - printk( "Irq 5 shouldn't be used by cdrom interface on ISP16," - " due to possible conflicts with the soundcard.\n"); - break; - case 7: irq_code = ISP16_IRQ_7; - printk( "Irq 7 shouldn't be used by cdrom interface on ISP16," - " due to possible conflicts with the soundcard.\n"); - break; - case 3: irq_code = ISP16_IRQ_3; break; - case 9: irq_code = ISP16_IRQ_9; break; - case 10: irq_code = ISP16_IRQ_10; break; - case 11: irq_code = ISP16_IRQ_11; break; - default: - printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq ); - return(-1); - } - switch (dma) { - case 0: dma_code = ISP16_DMA_X; break; /* disable dma */ - case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16," - " due to conflict with the soundcard.\n"); - return(-1); break; - case 3: dma_code = ISP16_DMA_3; break; - case 5: dma_code = ISP16_DMA_5; break; - case 6: dma_code = ISP16_DMA_6; break; - case 7: dma_code = ISP16_DMA_7; break; - default: - printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma ); - return(-1); - } - - if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 && - drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 && - drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI && - drive_type != ISP16_DRIVE_X ) { - printk( "Drive type (code 0x%02X) not supported by cdrom" - " interface on ISP16.\n", drive_type ); - return(-1); - } - - /* set type of interface */ - i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK; /* clear some bits */ - ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type ); - - /* enable cdrom on interface with 82C929 chip */ - if ( isp16_type > 1 ) - ISP16_OUT( isp16_enable_port, ISP16_ENABLE_CDROM ); - - /* set base address, irq and dma */ - i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK; /* keep some bits */ - ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code ); - - return(0); -} - -static void isp16_sound_config( void ) -{ - int i; - u_char saved; - - saved = ISP16_IN( 0xF8D ) & 0x8F; - - ISP16_OUT( 0xF8D, 0x40 ); - - /* - * Now we should wait for a while... - */ - for( i = 16*1024; i--; ); - - ISP16_OUT( 0xF8D, saved ); - - ISP16_OUT( 0xF91, 0x1B ); - - for( i = 5*64*1024; i != 0; i-- ) - if( !( inb( 0x534 ) & 0x80 ) ) break; - - if( i > 0 ) { - saved = ( inb( 0x534 ) & 0xE0 ) | 0x0A; - outb( saved, 0x534 ); - - special_mask = ( inb( 0x535 ) >> 4 ) & 0x08; - - saved = ( inb( 0x534 ) & 0xE0 ) | 0x0C; - outb( saved, 0x534 ); - - switch( inb( 0x535 ) ) { - case 0x09: - case 0x0A: - special_mask |= 0x05; - break; - case 0x8A: - special_mask = 0x0F; - break; - default: - i = 0; - } - } - if ( i == 0 ) { - printk( "Strange MediaMagic, but\n" ); - } - else { - printk( "Conf:" ); - saved = inb( 0x534 ) & 0xE0; - for( i = 0; i < 16; i++ ) { - outb( 0x20 | ( u_char )i, 0x534 ); - outb( defaults[i], 0x535 ); - } - for ( i = 0; i < 16; i++ ) { - outb( 0x20 | ( u_char )i, 0x534 ); - saved = inb( 0x535 ); - printk( " %02X", saved ); - } - printk( "\n" ); - } - - ISP16_OUT( 0xF91, 0xA0 | special_mask ); - - /* - * The following have no explaination yet. - */ - ISP16_OUT( 0xF90, 0xA2 ); - ISP16_OUT( 0xF92, 0x03 ); - - /* - * Turn general sound on and set total volume. - */ - ISP16_OUT( 0xF93, 0x0A ); - -/* - outb( 0x04, 0x224 ); - saved = inb( 0x225 ); - outb( 0x04, 0x224 ); - outb( saved, 0x225 ); -*/ - -} diff -u --recursive --new-file v1.3.35/linux/drivers/block/sonycd535.c linux/drivers/block/sonycd535.c --- v1.3.35/linux/drivers/block/sonycd535.c Tue Oct 10 18:46:32 1995 +++ linux/drivers/block/sonycd535.c Thu Jan 1 02:00:00 1970 @@ -1,1703 +0,0 @@ -/* - * Sony CDU-535 interface device driver - * - * This is a modified version of the CDU-31A device driver (see below). - * Changes were made using documentation for the CDU-531 (which Sony - * assures me is very similar to the 535) and partial disassembly of the - * DOS driver. I used Minyard's driver and replaced the the CDU-31A - * commands with the CDU-531 commands. This was complicated by a different - * interface protocol with the drive. The driver is still polled. - * - * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec. - * I tried polling without the sony_sleep during the data transfers but - * it did not speed things up any. - * - * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict - * with CDU-31A driver. This is the also the number from the Linux - * Device Driver Registry for the Sony Drive. Hope nobody else is using it. - * - * 1993-08-29 (rgj) remove the configuring of the interface board address - * from the top level configuration, you have to modify it in this file. - * - * 1995-01-26 Made module-capable (Joel Katz ) - * - * 1995-05-20 - * Modified to support CDU-510/515 series - * (Claudio Porfiri) - * Fixed to report verify_area() failures - * (Heiko Eissfeldt ) - * - * 1995-06-01 - * More changes to support CDU-510/515 series - * (Claudio Porfiri) - * - * 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) && (! - -#ifdef MODULE -# include -# include -# 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REALLY_SLOW_IO -#include -#include -#include - -#include - -#define MAJOR_NR CDU535_CDROM_MAJOR -# include "blk.h" -#define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */ -#include - -/* - * 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 - * . - */ - /* - * This part is to deal with very slow hardware. We - * try at most MAX_SPINUP_RETRY times to read the same - * block. A check for seek_and_read_N_blocks' result is - * performed; if the result is wrong, the CDROM's engine - * is restarted and the operation is tried again. - */ - /* - * 1995-06-01: The system got problems when downloading - * from Slackware CDROM, the problem seems to be: - * seek_and_read_N_blocks returns BAD_STATUS and we - * should wait for a while before retrying, so a new - * part was added to discriminate the return value from - * seek_and_read_N_blocks for the various cases. - */ - int readStatus = seek_and_read_N_blocks(params, read_size, - status, sony_buffer, (read_size * 2048)); - if (0 <= readStatus) /* Good data; common case, placed first */ - break; - if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) { - /* give up */ - if (readStatus == NO_ROOM) - printk(CDU535_MESSAGE_NAME " No room to read from CD\n"); - else - printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n", - status[0]); - sony_first_block = -1; - sony_last_block = -1; - end_request(0); - return; - } - if (readStatus == BAD_STATUS) { - /* Sleep for a while, then retry */ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + RETRY_FOR_BAD_STATUS; - schedule(); - } -#if DEBUG > 0 - printk(CDU535_MESSAGE_NAME - " debug: calling spin up when reading data!\n"); -#endif - cmd[0] = SONY535_SPIN_UP; - do_sony_cmd(cmd, 1, status, NULL, 0, 0); - } - } - /* - * The data is in memory now, copy it to the buffer and advance to the - * next block to read. - */ - copyoff = block - sony_first_block; - memcpy(CURRENT->buffer, - sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512); - - block += 1; - nsect -= 1; - CURRENT->buffer += 512; - } - - end_request(1); - break; - - case WRITE: - end_request(0); - break; - - default: - panic("Unknown SONY CD cmd"); - } - } -} - - -/* - * Read the table of contents from the drive and set sony_toc_read if - * successful. - */ -static void -sony_get_toc(void) -{ - Byte status[2]; - if (!sony_toc_read) { - /* do not call check_drive_status() from here since it can call this routine */ - if (request_toc_data(status, sony_toc) < 0) - return; - sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); - sony_toc_read = 1; - } -} - - -/* - * Search for a specific track in the table of contents. track is - * passed in bcd format - */ -static int -find_track(int track) -{ - int i; - int num_tracks; - - - num_tracks = bcd_to_int(sony_toc->last_track_num) - - bcd_to_int(sony_toc->first_track_num) + 1; - for (i = 0; i < num_tracks; i++) { - if (sony_toc->tracks[i].track == track) { - return i; - } - } - - return -1; -} - -/* - * Read the subcode and put it int last_sony_subcode for future use. - */ -static int -read_subcode(void) -{ - Byte cmd = SONY535_REQUEST_SUB_Q_DATA; - Byte status[2]; - int dsc_status; - - if (check_drive_status() != 0) - return -EIO; - - if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode, - sizeof(struct s535_sony_subcode), 1)) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n", - status[0], dsc_status); - return -EIO; - } - return 0; -} - - -/* - * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If - * the drive is playing, the subchannel needs to be read (since it would be - * changing). If the drive is paused or completed, the subcode information has - * already been stored, just use that. The ioctl call wants things in decimal - * (not BCD), so all the conversions are done. - */ -static int -sony_get_subchnl_info(long arg) -{ - struct cdrom_subchnl schi; - int err; - - /* Get attention stuff */ - if (check_drive_status() != 0) - return -EIO; - - sony_get_toc(); - if (!sony_toc_read) { - return -EIO; - } - err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi); - if (err) - return err; - - memcpy_fromfs(&schi, (char *)arg, sizeof schi); - - switch (sony_audio_status) { - case CDROM_AUDIO_PLAY: - if (read_subcode() < 0) { - return -EIO; - } - break; - - case CDROM_AUDIO_PAUSED: - case CDROM_AUDIO_COMPLETED: - break; - - case CDROM_AUDIO_NO_STATUS: - schi.cdsc_audiostatus = sony_audio_status; - memcpy_tofs((char *)arg, &schi, sizeof schi); - return 0; - break; - - case CDROM_AUDIO_INVALID: - case CDROM_AUDIO_ERROR: - default: - return -EIO; - } - - schi.cdsc_audiostatus = sony_audio_status; - schi.cdsc_adr = last_sony_subcode->address; - schi.cdsc_ctrl = last_sony_subcode->control; - schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num); - schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num); - if (schi.cdsc_format == CDROM_MSF) { - schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]); - schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]); - schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]); - - schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]); - schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]); - schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]); - } else if (schi.cdsc_format == CDROM_LBA) { - schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf); - schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf); - } - memcpy_tofs((char *)arg, &schi, sizeof schi); - return 0; -} - - -/* - * The big ugly ioctl handler. - */ -static int -cdu_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - unsigned int dev; - Byte status[2]; - Byte cmd_buff[10], params[10]; - int i; - int dsc_status; - int err; - - if (!inode) { - return -EINVAL; - } - dev = MINOR(inode->i_rdev) >> 6; - if (dev != 0) { - return -EINVAL; - } - if (check_drive_status() != 0) - return -EIO; - - switch (cmd) { - case CDROMSTART: /* Spin up the drive */ - if (spin_up_drive(status) < 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n", - status[0]); - return -EIO; - } - return 0; - break; - - case CDROMSTOP: /* Spin down the drive */ - cmd_buff[0] = SONY535_HOLD; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - - /* - * Spin the drive down, ignoring the error if the disk was - * already not spinning. - */ - sony_audio_status = CDROM_AUDIO_NO_STATUS; - cmd_buff[0] = SONY535_SPIN_DOWN; - dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) || - ((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n", - status[0]); - return -EIO; - } - return 0; - break; - - case CDROMPAUSE: /* Pause the drive */ - cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */ - if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n", - status[0]); - return -EIO; - } - /* Get the current position and save it for resuming */ - if (read_subcode() < 0) { - return -EIO; - } - cur_pos_msf[0] = last_sony_subcode->abs_msf[0]; - cur_pos_msf[1] = last_sony_subcode->abs_msf[1]; - cur_pos_msf[2] = last_sony_subcode->abs_msf[2]; - sony_audio_status = CDROM_AUDIO_PAUSED; - return 0; - break; - - case CDROMRESUME: /* Start the drive after being paused */ - set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); - - if (sony_audio_status != CDROM_AUDIO_PAUSED) { - return -EINVAL; - } - spin_up_drive(status); - - /* Start the drive at the saved position. */ - cmd_buff[0] = SONY535_PLAY_AUDIO; - cmd_buff[1] = 0; /* play back starting at this address */ - cmd_buff[2] = cur_pos_msf[0]; - cmd_buff[3] = cur_pos_msf[1]; - cmd_buff[4] = cur_pos_msf[2]; - cmd_buff[5] = SONY535_PLAY_AUDIO; - cmd_buff[6] = 2; /* set ending address */ - cmd_buff[7] = final_pos_msf[0]; - cmd_buff[8] = final_pos_msf[1]; - cmd_buff[9] = final_pos_msf[2]; - if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || - (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n", - status[0]); - return -EIO; - } - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - break; - - case CDROMPLAYMSF: /* Play starting at the given MSF address. */ - err = verify_area(VERIFY_READ, (char *)arg, 6); - if (err) - return err; - spin_up_drive(status); - set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); - memcpy_fromfs(params, (void *)arg, 6); - - /* The parameters are given in int, must be converted */ - for (i = 0; i < 3; i++) { - cmd_buff[2 + i] = int_to_bcd(params[i]); - cmd_buff[7 + i] = int_to_bcd(params[i + 3]); - } - cmd_buff[0] = SONY535_PLAY_AUDIO; - cmd_buff[1] = 0; /* play back starting at this address */ - /* cmd_buff[2-4] are filled in for loop above */ - cmd_buff[5] = SONY535_PLAY_AUDIO; - cmd_buff[6] = 2; /* set ending address */ - /* cmd_buff[7-9] are filled in for loop above */ - if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || - (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n", - status[0]); - return -EIO; - } - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = cmd_buff[7]; - final_pos_msf[1] = cmd_buff[8]; - final_pos_msf[2] = cmd_buff[9]; - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - break; - - case CDROMREADTOCHDR: /* Read the table of contents header */ - { - struct cdrom_tochdr *hdr; - struct cdrom_tochdr loc_hdr; - - sony_get_toc(); - if (!sony_toc_read) - return -EIO; - hdr = (struct cdrom_tochdr *)arg; - err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr); - if (err) - return err; - loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); - loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); - memcpy_tofs(hdr, &loc_hdr, sizeof *hdr); - } - return 0; - break; - - case CDROMREADTOCENTRY: /* Read a given table of contents entry */ - { - struct cdrom_tocentry *entry; - struct cdrom_tocentry loc_entry; - int track_idx; - Byte *msf_val = NULL; - - sony_get_toc(); - if (!sony_toc_read) { - return -EIO; - } - entry = (struct cdrom_tocentry *)arg; - err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry); - if (err) - return err; - - memcpy_fromfs(&loc_entry, entry, sizeof loc_entry); - - /* Lead out is handled separately since it is special. */ - if (loc_entry.cdte_track == CDROM_LEADOUT) { - loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ; - loc_entry.cdte_ctrl = sony_toc->control2; - msf_val = sony_toc->lead_out_start_msf; - } else { - track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); - if (track_idx < 0) - return -EINVAL; - loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ; - loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; - msf_val = sony_toc->tracks[track_idx].track_start_msf; - } - - /* Logical buffer address or MSF format requested? */ - if (loc_entry.cdte_format == CDROM_LBA) { - loc_entry.cdte_addr.lba = msf_to_log(msf_val); - } else if (loc_entry.cdte_format == CDROM_MSF) { - loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); - loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1)); - loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2)); - } - memcpy_tofs(entry, &loc_entry, sizeof *entry); - } - return 0; - break; - - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - { - struct cdrom_ti ti; - int track_idx; - - sony_get_toc(); - if (!sony_toc_read) - return -EIO; - err = verify_area(VERIFY_READ, (char *)arg, sizeof ti); - if (err) - return err; - - memcpy_fromfs(&ti, (char *)arg, sizeof ti); - if ((ti.cdti_trk0 < sony_toc->first_track_num) - || (sony_toc->last_track_num < ti.cdti_trk0) - || (ti.cdti_trk1 < ti.cdti_trk0)) { - return -EINVAL; - } - track_idx = find_track(int_to_bcd(ti.cdti_trk0)); - if (track_idx < 0) - return -EINVAL; - params[1] = sony_toc->tracks[track_idx].track_start_msf[0]; - params[2] = sony_toc->tracks[track_idx].track_start_msf[1]; - params[3] = sony_toc->tracks[track_idx].track_start_msf[2]; - /* - * If we want to stop after the last track, use the lead-out - * MSF to do that. - */ - if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) { - log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1, - &(params[4])); - } else { - track_idx = find_track(int_to_bcd(ti.cdti_trk1 + 1)); - if (track_idx < 0) - return -EINVAL; - log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf) - 1, - &(params[4])); - } - params[0] = 0x03; - - spin_up_drive(status); - - set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); - - /* Start the drive at the saved position. */ - cmd_buff[0] = SONY535_PLAY_AUDIO; - cmd_buff[1] = 0; /* play back starting at this address */ - cmd_buff[2] = params[1]; - cmd_buff[3] = params[2]; - cmd_buff[4] = params[3]; - cmd_buff[5] = SONY535_PLAY_AUDIO; - cmd_buff[6] = 2; /* set ending address */ - cmd_buff[7] = params[4]; - cmd_buff[8] = params[5]; - cmd_buff[9] = params[6]; - if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || - (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n", - status[0]); - printk("... Params: %x %x %x %x %x %x %x\n", - params[0], params[1], params[2], - params[3], params[4], params[5], params[6]); - return -EIO; - } - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = params[4]; - final_pos_msf[1] = params[5]; - final_pos_msf[2] = params[6]; - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - } - - case CDROMSUBCHNL: /* Get subchannel info */ - return sony_get_subchnl_info(arg); - - case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ - { - struct cdrom_volctrl volctrl; - - err = verify_area(VERIFY_READ, (char *)arg, sizeof volctrl); - if (err) - return err; - - memcpy_fromfs(&volctrl, (char *)arg, sizeof volctrl); - cmd_buff[0] = SONY535_SET_VOLUME; - cmd_buff[1] = volctrl.channel0; - cmd_buff[2] = volctrl.channel1; - if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n", - status[0]); - return -EIO; - } - } - return 0; - - case CDROMEJECT: /* Eject the drive */ - cmd_buff[0] = SONY535_STOP; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - cmd_buff[0] = SONY535_SPIN_DOWN; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - - sony_audio_status = CDROM_AUDIO_INVALID; - cmd_buff[0] = SONY535_EJECT_CADDY; - if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n", - status[0]); - return -EIO; - } - return 0; - break; - - default: - return -EINVAL; - } -} - - -/* - * Open the drive for operations. Spin the drive up and read the table of - * contents if these have not already been done. - */ -static int -cdu_open(struct inode *inode, - struct file *filp) -{ - Byte status[2], cmd_buff[2]; - - - if (sony_inuse) - return -EBUSY; - if (check_drive_status() != 0) - return -EIO; - sony_inuse = 1; - MOD_INC_USE_COUNT; - - if (spin_up_drive(status) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n", - status[0]); - sony_inuse = 0; - MOD_DEC_USE_COUNT; - return -EIO; - } - sony_get_toc(); - if (!sony_toc_read) { - cmd_buff[0] = SONY535_SPIN_DOWN; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - sony_inuse = 0; - MOD_DEC_USE_COUNT; - return -EIO; - } - if (inode) { - check_disk_change(inode->i_rdev); - } - sony_usage++; - -#ifdef LOCK_DOORS - /* disable the eject button while mounted */ - cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); -#endif - - return 0; -} - - -/* - * Close the drive. Spin it down if no task is using it. The spin - * down will fail if playing audio, so audio play is OK. - */ -static void -cdu_release(struct inode *inode, - struct file *filp) -{ - Byte status[2], cmd_no; - - sony_inuse = 0; - MOD_DEC_USE_COUNT; - - if (0 < sony_usage) { - sony_usage--; - } - if (sony_usage == 0) { - sync_dev(inode->i_rdev); - check_drive_status(); - - if (sony_audio_status != CDROM_AUDIO_PLAY) { - cmd_no = SONY535_SPIN_DOWN; - do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); - } -#ifdef LOCK_DOORS - /* enable the eject button after umount */ - cmd_no = SONY535_ENABLE_EJECT_BUTTON; - do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); -#endif - } -} - - -static struct file_operations cdu_fops = -{ - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* select */ - cdu_ioctl, /* ioctl */ - NULL, /* mmap */ - cdu_open, /* open */ - cdu_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - cdu535_check_media_change, /* check media change */ - NULL /* revalidate */ -}; - -/* - * Initialize the driver. - */ -int -sony535_init(void) -{ - struct s535_sony_drive_config drive_config; - Byte cmd_buff[3]; - Byte ret_buff[2]; - Byte status[2]; - int retry_count; - int tmp_irq; - int i; - - /* Setting the base I/O address to 0 will disable it. */ - if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0)) - return 0; - - /* Set up all the register locations */ - result_reg = sony535_cd_base_io; - command_reg = sony535_cd_base_io; - data_reg = sony535_cd_base_io + 1; - read_status_reg = sony535_cd_base_io + 2; - select_unit_reg = sony535_cd_base_io + 3; - -#ifndef USE_IRQ - sony535_irq_used = 0; /* polling only until this is ready... */ -#endif - /* we need to poll until things get initialized */ - tmp_irq = sony535_irq_used; - sony535_irq_used = 0; - -#if DEBUG > 0 - printk(CDU535_MESSAGE_NAME ": probing base address %03X\n", - sony535_cd_base_io); -#endif - if (check_region(sony535_cd_base_io,4)) { - printk(CDU535_MESSAGE_NAME ": my base address is not free!\n"); - return -EIO; - } - /* look for the CD-ROM, follows the procedure in the DOS driver */ - inb(select_unit_reg); - retry_count = jiffies + 2 * HZ; - while (jiffies < retry_count) - sony_sleep(); /* wait for 40 18 Hz ticks (from DOS driver) */ - inb(result_reg); - - outb(0, read_status_reg); /* does a reset? */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while (jiffies < retry_count) { - select_unit(0); - if (inb(result_reg) != 0xff) - break; - sony_sleep(); - } - - if ((jiffies < retry_count) && (check_drive_status() != TIME_OUT)) { - /* CD-ROM drive responded -- get the drive configuration */ - cmd_buff[0] = SONY535_INQUIRY; - if (do_sony_cmd(cmd_buff, 1, status, - (Byte *)&drive_config, 28, 1) == 0) { - /* was able to get the configuration, - * set drive mode as rest of init - */ -#if DEBUG > 0 - /* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */ - if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 ) - printk(CDU535_MESSAGE_NAME - "Inquiry command returned status = 0x%x\n", status[0]); -#endif - /* now ready to use interrupts, if available */ - sony535_irq_used = tmp_irq; -#ifndef MODULE -/* This code is not in MODULEs by default, since the autoirq stuff might - * not be in the module-accessible symbol table. - */ - /* A negative sony535_irq_used will attempt an autoirq. */ - if (sony535_irq_used < 0) { - autoirq_setup(0); - enable_interrupts(); - outb(0, read_status_reg); /* does a reset? */ - sony535_irq_used = autoirq_report(10); - disable_interrupts(); - } -#endif - if (sony535_irq_used > 0) { - if (request_irq(sony535_irq_used, cdu535_interrupt, - SA_INTERRUPT, CDU535_HANDLE)) { - printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME - " driver; polling instead.\n", sony535_irq_used); - sony535_irq_used = 0; - } - } - cmd_buff[0] = SONY535_SET_DRIVE_MODE; - cmd_buff[1] = 0x0; /* default audio */ - if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) { - /* set the drive mode successful, we are set! */ - sony_buffer_size = SONY535_BUFFER_SIZE; - sony_buffer_sectors = sony_buffer_size / 2048; - - printk(CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s", - drive_config.vendor_id, - drive_config.product_id, - drive_config.product_rev_level); - printk(" base address %03X, ", sony535_cd_base_io); - if (tmp_irq > 0) - printk("IRQ%d, ", tmp_irq); - printk("using %d byte buffer\n", sony_buffer_size); - - if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) { - printk("Unable to get major %d for %s\n", - MAJOR_NR, CDU535_MESSAGE_NAME); - return -EIO; - } - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ - - sony_toc = (struct s535_sony_toc *) - kmalloc(sizeof *sony_toc, GFP_KERNEL); - if (sony_toc == NULL) - return -ENOMEM; - last_sony_subcode = (struct s535_sony_subcode *) - kmalloc(sizeof *last_sony_subcode, GFP_KERNEL); - if (last_sony_subcode == NULL) { - kfree(sony_toc); - return -ENOMEM; - } - sony_buffer = (Byte **) - kmalloc(4 * sony_buffer_sectors, GFP_KERNEL); - if (sony_buffer == NULL) { - kfree(sony_toc); - kfree(last_sony_subcode); - return -ENOMEM; - } - for (i = 0; i < sony_buffer_sectors; i++) { - sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL); - if (sony_buffer[i] == NULL) { - while (--i>=0) - kfree(sony_buffer[i]); - kfree(sony_buffer); - kfree(sony_toc); - kfree(last_sony_subcode); - return -ENOMEM; - } - } - initialized = 1; - } - } - } - - if (!initialized) { - printk("Did not find a " CDU535_MESSAGE_NAME " drive\n"); - return -EIO; - } - request_region(sony535_cd_base_io, 4, CDU535_HANDLE); - return 0; -} - -#ifndef MODULE -/* - * accept "kernel command line" parameters - * (added by emoenke@gwdg.de) - * - * use: tell LILO: - * sonycd535=0x320 - * - * the address value has to be the existing CDROM port address. - */ -void -sonycd535_setup(char *strings, int *ints) -{ - /* if IRQ change and default io base desired, - * then call with io base of 0 - */ - if (ints[0] > 0) - if (ints[0] != 0) - sony535_cd_base_io = ints[1]; - if (ints[0] > 1) - sony535_irq_used = ints[2]; - if ((strings != NULL) && (*strings != '\0')) - printk(CDU535_MESSAGE_NAME - ": Warning: Unknown interface type: %s\n", strings); -} - -#else /* MODULE */ - -void -cleanup_module(void) -{ - int i; - if (MOD_IN_USE) { - printk(CDU535_HANDLE " module in use, cannot remove\n"); - return; - } - release_region(sony535_cd_base_io, 4); - for (i = 0; i < sony_buffer_sectors; i++) - kfree_s(sony_buffer[i], 2048); - kfree_s(sony_buffer, 4 * sony_buffer_sectors); - kfree_s(last_sony_subcode, sizeof *last_sony_subcode); - kfree_s(sony_toc, sizeof *sony_toc); - if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL) - printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n"); - else - printk(CDU535_HANDLE " module released\n"); -} -#endif /* MODULE */ diff -u --recursive --new-file v1.3.35/linux/drivers/block/xd.c linux/drivers/block/xd.c --- v1.3.35/linux/drivers/block/xd.c Tue Oct 10 18:46:33 1995 +++ linux/drivers/block/xd.c Sat Oct 21 19:37:35 1995 @@ -34,7 +34,7 @@ #include #define MAJOR_NR XT_DISK_MAJOR -#include "blk.h" +#include XD_INFO xd_info[XD_MAXDRIVES]; diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/Config.in linux/drivers/cdrom/Config.in --- v1.3.35/linux/drivers/cdrom/Config.in Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/Config.in Sun Oct 22 16:02:13 1995 @@ -0,0 +1,22 @@ +# +# CDROM driver configuration +# +tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A +tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD +tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX +tristate 'Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD +if [ "$CONFIG_SBPCD" = "y" ]; then + bool 'Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2 + if [ "$CONFIG_SBPCD2" = "y" ]; then + bool 'Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3 + if [ "$CONFIG_SBPCD3" = "y" ]; then + bool 'Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4 + fi + fi +fi +tristate 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD +tristate 'Sony CDU535 CDROM support' CONFIG_CDU535 +tristate 'Goldstar R420 CDROM support' CONFIG_GSCD +tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206 +tristate 'Experimental Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD +tristate 'Experimental Sanyo H94A CDROM support' CONFIG_SJCD diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile --- v1.3.35/linux/drivers/cdrom/Makefile Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/Makefile Sat Oct 21 17:51:35 1995 @@ -0,0 +1,115 @@ +# +# Makefile for the kernel cdrom device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now inherited from the +# parent makefile. +# + +# +# Note : at this point, these files are compiled on all systems. +# In the future, some of these should be built conditionally. +# + + +L_TARGET := cdrom.a +L_OBJS := +M_OBJS := +MOD_LIST_NAME := CDROM_MODULES + +ifeq ($(CONFIG_AZTCD),y) +L_OBJS += aztcd.o +else + ifeq ($(CONFIG_AZTCD),m) + M_OBJS += aztcd.o + endif +endif #CONFIG_AZTCD + +ifeq ($(CONFIG_CDU31A),y) +L_OBJS += cdu31a.o +else + ifeq ($(CONFIG_CDU31A),m) + M_OBJS += cdu31a.o + endif +endif #CONFIG_CDU31A + +ifeq ($(CONFIG_MCD),y) +L_OBJS += mcd.o +else + ifeq ($(CONFIG_MCD),m) + M_OBJS += mcd.o + endif +endif #CONFIG_MCD + +ifeq ($(CONFIG_MCDX),y) +L_OBJS += mcdx.o +else + ifeq ($(CONFIG_MCDX),m) + M_OBJS += mcdx.o + endif +endif #CONFIG_MCDX + +ifeq ($(CONFIG_SBPCD),y) +L_OBJS += sbpcd.o +else + ifeq ($(CONFIG_SBPCD),m) + M_OBJS += sbpcd.o + endif +endif #CONFIG_SBPCD + +ifeq ($(CONFIG_SBPCD2),y) +L_OBJS += sbpcd2.o +endif #CONFIG_SBPCD2 + +ifeq ($(CONFIG_SBPCD3),y) +L_OBJS += sbpcd3.o +endif #CONFIG_SBPCD3 + +ifeq ($(CONFIG_SBPCD4),y) +L_OBJS += sbpcd4.o +endif #CONFIG_SBPCD4 + +ifeq ($(CONFIG_CDU535),y) +L_OBJS += sonycd535.o +else + ifeq ($(CONFIG_CDU535),m) + M_OBJS += sonycd535.o + endif +endif #CONFIG_CDU535 + +ifeq ($(CONFIG_GSCD),y) +L_OBJS += gscd.o +else + ifeq ($(CONFIG_GSCD),m) + M_OBJS += gscd.o + endif +endif #CONFIG_GSCD + +ifeq ($(CONFIG_CM206),y) +L_OBJS += cm206.o +else + ifeq ($(CONFIG_CM206),m) + M_OBJS += cm206.o + endif +endif #CONFIG_CM206 + +ifeq ($(CONFIG_OPTCD),y) +L_OBJS += optcd.o +else + ifeq ($(CONFIG_OPTCD),m) + M_OBJS += optcd.o + endif +endif #CONFIG_OPTCD + +ifeq ($(CONFIG_SJCD),y) +L_OBJS += sjcd.o +#else +# ifeq ($(CONFIG_SJCD),m) +# M_OBJS += sjcd.o +# endif +endif #CONFIG_SJCD + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c --- v1.3.35/linux/drivers/cdrom/aztcd.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/aztcd.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,2087 @@ +#define AZT_VERSION "1.80" +/* $Id: aztcd.c,v 1.80 1995/10/11 19:35:03 root Exp root $ + linux/drivers/block/aztcd.c - AztechCD268 CDROM driver + + Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de) + + based on Mitsumi CDROM driver by Martin Hariss and preworks by + Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby + Schirmer. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + HISTORY + V0.0 Adaption to Adaptec CD268-01A Version 1.3 + Version is PRE_ALPHA, unresolved points: + 1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW + thus driver causes CPU overhead and is very slow + 2. could not find a way to stop the drive, when it is + in data read mode, therefore I had to set + msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one + frame can be read in sequence, this is also the reason for + 3. getting 'timeout in state 4' messages, but nevertheless + it works + W.Zimmermann, Oct. 31, 1994 + V0.1 Version is ALPHA, problems #2 and #3 resolved. + W.Zimmermann, Nov. 3, 1994 + V0.2 Modification to some comments, debugging aids for partial test + with Borland C under DOS eliminated. Timer interrupt wait + STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented; + use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_ + SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy + waiting seems better to me than interrupt rescheduling. + Besides that, when used in the wrong place, STEN_LOW_WAIT causes + kernel panic. + In function aztPlay command ACMD_PLAY_AUDIO added, should make + audio functions work. The Aztech drive needs different commands + to read data tracks and play audio tracks. + W.Zimmermann, Nov. 8, 1994 + V0.3 Recognition of missing drive during boot up improved (speeded up). + W.Zimmermann, Nov. 13, 1994 + V0.35 Rewrote the control mechanism in azt_poll (formerly mcd_poll) + including removal of all 'goto' commands. :-); + J. Nardone, Nov. 14, 1994 + V0.4 Renamed variables and constants to 'azt' instead of 'mcd'; had + to make some "compatibility" defines in azt.h; please note, + that the source file was renamed to azt.c, the include file to + azt.h + Speeded up drive recognition during init (will be a little bit + slower than before if no drive is installed!); suggested by + Robby Schirmer. + read_count declared volatile and set to AZT_BUF_SIZ to make + drive faster (now 300kB/sec, was 60kB/sec before, measured + by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096'; + different AZT_BUF_SIZes were test, above 16 no further im- + provement seems to be possible; suggested by E.Moenkeberg. + W.Zimmermann, Nov. 18, 1994 + V0.42 Included getAztStatus command in GetQChannelInfo() to allow + reading Q-channel info on audio disks, if drive is stopped, + and some other bug fixes in the audio stuff, suggested by + Robby Schirmer. + Added more ioctls (reading data in mode 1 and mode 2). + Completely removed the old azt_poll() routine. + Detection of ORCHID CDS-3110 in aztcd_init implemented. + Additional debugging aids (see the readme file). + W.Zimmermann, Dec. 9, 1994 + V0.50 Autodetection of drives implemented. + W.Zimmermann, Dec. 12, 1994 + V0.52 Prepared for including in the standard kernel, renamed most + variables to contain 'azt', included autoconf.h + W.Zimmermann, Dec. 16, 1994 + V0.6 Version for being included in the standard Linux kernel. + Renamed source and header file to aztcd.c and aztcd.h + W.Zimmermann, Dec. 24, 1994 + V0.7 Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case + CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl, + which causes kernel crashes when playing audio, changed + include-files (config.h instead of autoconf.h, removed + delay.h) + W.Zimmermann, Jan. 8, 1995 + V0.72 Some more modifications for adaption to the standard kernel. + W.Zimmermann, Jan. 16, 1995 + V0.80 aztcd is now part of the standard kernel since version 1.1.83. + Modified the SET_TIMER and CLEAR_TIMER macros to comply with + the new timer scheme. + W.Zimmermann, Jan. 21, 1995 + V0.90 Included CDROMVOLCTRL, but with my Aztech drive I can only turn + the channels on and off. If it works better with your drive, + please mail me. Also implemented ACMD_CLOSE for CDROMSTART. + W.Zimmermann, Jan. 24, 1995 + V1.00 Implemented close and lock tray commands. Patches supplied by + Frank Racis + Added support for loadable MODULEs, so aztcd can now also be + loaded by insmod and removed by rmmod during run time + Werner Zimmermann, Mar. 24, 95 + V1.10 Implemented soundcard configuration for Orchid CDS-3110 drives + connected to Soundwave32 cards. Release for LST 2.1. + (still experimental) + Werner Zimmermann, May 8, 95 + V1.20 Implemented limited support for DOSEMU0.60's cdrom.c. Now it works, but + sometimes DOSEMU may hang for 30 seconds or so. A fully functional ver- + sion needs an update of Dosemu0.60's cdrom.c, which will come with the + next revision of Dosemu. + Also Soundwave32 support now works. + Werner Zimmermann, May 22, 95 + V1.30 Auto-eject feature. Inspired by Franc Racis (racis@psu.edu) + Werner Zimmermann, July 4, 95 + V1.40 Started multisession support. Implementation copied from mcdx.c + by Heiko Schlittermann. Not tested yet. + Werner Zimmermann, July 15, 95 + V1.50 Implementation of ioctl CDROMRESET, continued multisession, began + XA, but still untested. Heavy modifications to drive status de- + tection. + Werner Zimmermann, July 25, 95 + V1.60 XA support now should work. Speeded up drive recognition in cases, + where no drive is installed. + Werner Zimmermann, August 8, 1995 + V1.70 Multisession support now is completed, but there is still not + enough testing done. If you can test it, please contact me. For + details please read README.aztcd. + Werner Zimmermann, August 19, 1995 + V1.80 Modification to suit the new kernel boot procedure introduced + with kernel 1.3.33. Will definitely not work with older kernels. + Programming done by Linus himself. + Werner Zimmermann, October 11, 1995 + NOTE: + Points marked with ??? are questionable ! +*/ +#include +#include + +#ifdef MODULE +# include +# include +# ifndef CONFIG_MODVERSIONS + char kernel_version[]= UTS_RELEASE; +# endif +#define aztcd_init init_module +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAJOR_NR AZTECH_CDROM_MAJOR +#include + +#ifdef MODULE +#else +# define MOD_INC_USE_COUNT +# define MOD_DEC_USE_COUNT +#endif + +#include + +#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 /* */ +#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) + { 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 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 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) + { 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 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; ii_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=,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,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;st0;limit--) + { if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetDiskInfo 2",-1); + if (qInfo.pointIndex==0xA0) /*Number of FirstTrack*/ + { DiskInfo.first = qInfo.diskTime.min; + DiskInfo.first = azt_bcd2bin(DiskInfo.first); + test=test|0x01; + } + if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/ + { DiskInfo.last = qInfo.diskTime.min; + DiskInfo.last = azt_bcd2bin(DiskInfo.last); + test=test|0x02; + } + if (qInfo.pointIndex==0xA2) /*DiskLength*/ + { DiskInfo.diskLength.min=qInfo.diskTime.min; + DiskInfo.diskLength.sec=qInfo.diskTime.sec; + DiskInfo.diskLength.frame=qInfo.diskTime.frame; + test=test|0x04; + } + if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01)) /*StartTime of First Track*/ + { DiskInfo.firstTrack.min=qInfo.diskTime.min; + DiskInfo.firstTrack.sec=qInfo.diskTime.sec; + DiskInfo.firstTrack.frame=qInfo.diskTime.frame; + test=test|0x08; + } + if (test==0x0F) break; + } +#ifdef AZT_DEBUG + printk ("aztcd: exiting aztGetDiskInfo Time:%li\n",jiffies); + printk("Disk Info: first %d last %d length %02X:%02X.%02X dez first %02X:%02X.%02X dez\n", + DiskInfo.first, + DiskInfo.last, + DiskInfo.diskLength.min, + DiskInfo.diskLength.sec, + DiskInfo.diskLength.frame, + DiskInfo.firstTrack.min, + DiskInfo.firstTrack.sec, + DiskInfo.firstTrack.frame); +#endif + if (test!=0x0F) return -1; + return 0; +} + +#if AZT_MULTISESSION +/* + * Get Multisession Disk Info + */ +static int aztGetMultiDiskInfo(void) +{ int limit, k=5; + unsigned char test; + struct azt_Toc qInfo; + +#ifdef AZT_DEBUG + printk("aztcd: starting aztGetMultiDiskInfo\n"); +#endif + + do { azt_Play.start.min = Toc[DiskInfo.last+1].diskTime.min; + azt_Play.start.sec = Toc[DiskInfo.last+1].diskTime.sec; + azt_Play.start.frame = Toc[DiskInfo.last+1].diskTime.frame; + test=0; + + for (limit=30;limit>0;limit--) /*Seek for LeadIn of next session*/ + { if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 1",-1); + if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 2",-1); + if ((qInfo.track==0)&&(qInfo.pointIndex)) break; /*LeadIn found*/ + if ((azt_Play.start.sec+=10) > 59) + { azt_Play.start.sec=0; + azt_Play.start.min++; + } + } + if (!limit) break; /*Check, if a leadin track was found, if not we're + at the end of the disk*/ +#ifdef AZT_DEBUG_MULTISESSION + printk("leadin found track %d pointIndex %x limit %d\n",qInfo.track,qInfo.pointIndex,limit); +#endif + for (limit=300;limit>0;limit--) + { if (++azt_Play.start.frame>74) + { azt_Play.start.frame=0; + if (azt_Play.start.sec > 59) + { azt_Play.start.sec=0; + azt_Play.start.min++; + } + } + if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 3",-1); + if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 4",-1); + if (qInfo.pointIndex==0xA0) /*Number of NextTrack*/ + { DiskInfo.next = qInfo.diskTime.min; + DiskInfo.next = azt_bcd2bin(DiskInfo.next); + test=test|0x01; + } + if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/ + { DiskInfo.last = qInfo.diskTime.min; + DiskInfo.last = azt_bcd2bin(DiskInfo.last); + test=test|0x02; + } + if (qInfo.pointIndex==0xA2) /*DiskLength*/ + { DiskInfo.diskLength.min =qInfo.diskTime.min; + DiskInfo.diskLength.sec =qInfo.diskTime.sec; + DiskInfo.diskLength.frame=qInfo.diskTime.frame; + test=test|0x04; + } + if ((qInfo.pointIndex==DiskInfo.next)&&(test&0x01)) /*StartTime of Next Track*/ + { DiskInfo.nextSession.min=qInfo.diskTime.min; + DiskInfo.nextSession.sec=qInfo.diskTime.sec; + DiskInfo.nextSession.frame=qInfo.diskTime.frame; + test=test|0x08; + } + if (test==0x0F) break; + } +#ifdef AZT_DEBUG_MULTISESSION + printk ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez first %02x:%02x.%02x dez next %02x:%02x.%02x dez\n", + DiskInfo.first, + DiskInfo.next, + DiskInfo.last, + DiskInfo.diskLength.min, + DiskInfo.diskLength.sec, + DiskInfo.diskLength.frame, + DiskInfo.firstTrack.min, + DiskInfo.firstTrack.sec, + DiskInfo.firstTrack.frame, + DiskInfo.nextSession.min, + DiskInfo.nextSession.sec, + DiskInfo.nextSession.frame); +#endif + if (test!=0x0F) + break; + else + DiskInfo.multi=1; /*found TOC of more than one session*/ + aztGetToc(1); + } while(--k); + +#ifdef AZT_DEBUG + printk ("aztcd: exiting aztGetMultiDiskInfo Time:%li\n",jiffies); +#endif + return 0; +} +#endif + +/* + * Read the table of contents (TOC) + */ +static int aztGetToc(int multi) +{ int i, px; + int limit; + struct azt_Toc qInfo; + +#ifdef AZT_DEBUG + printk("aztcd: starting aztGetToc Time:%li\n",jiffies); +#endif + if (!multi) + { for (i = 0; i < MAX_TRACKS; i++) + Toc[i].pointIndex = 0; + i = DiskInfo.last + 3; + } + else + { for (i = DiskInfo.next; i < MAX_TRACKS; i++) + Toc[i].pointIndex = 0; + i = DiskInfo.last + 4 - DiskInfo.next; + } + +/*Is there a good reason to stop motor before TOC read? + if (aztSendCmd(ACMD_STOP)) RETURNM("aztGetToc 1",-1); + STEN_LOW_WAIT; +*/ + + if (!multi) + { azt_mode = 0x05; + if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1); /*???*/ + STEN_LOW_WAIT; + } + for (limit = 300; limit > 0; limit--) + { if (multi) + { if (++azt_Play.start.sec > 59) + { azt_Play.start.sec=0; + azt_Play.start.min++; + } + if (aztSeek(&azt_Play)) RETURNM("aztGetToc 3",-1); + } + if (aztGetQChannelInfo(&qInfo) < 0) + break; + + px = azt_bcd2bin(qInfo.pointIndex); + + if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) + if (Toc[px].pointIndex == 0) + { Toc[px] = qInfo; + i--; + } + + if (i <= 0) + break; + } + + Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; + + +#ifdef AZT_DEBUG_MULTISESSION + printk("aztcd: exiting aztGetToc\n"); + for (i = 1; i <= DiskInfo.last+1; i++) + printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", + i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, + Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, + Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); + for (i = 100; i < 103; i++) + printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", + i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, + Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, + Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); +#endif + + return limit > 0 ? 0 : -1; +} + +#ifdef MODULE +void cleanup_module(void) +{ if (MOD_IN_USE) + { printk("aztcd module in use - can't remove it.\n"); + return; + } + if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) + { printk("What's that: can't unregister aztcd\n"); + return; + } + release_region(azt_port,4); + printk("aztcd module released.\n"); +} +#endif MODULE diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c --- v1.3.35/linux/drivers/cdrom/cdu31a.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/cdu31a.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,3100 @@ +/* + * Sony CDU-31A CDROM interface device driver. + * + * Corey Minyard (minyard@wf-rch.cirr.com) + * + * Colossians 3:17 + * + * The Sony interface device driver handles Sony interface CDROM + * drives and provides a complete block-level interface as well as an + * ioctl() interface compatible with the Sun (as specified in + * include/linux/cdrom.h). With this interface, CDROMs can be + * accessed and standard audio CDs can be played back normally. + * + * WARNING - All autoprobes have been removed from the driver. + * You MUST configure the CDU31A via a LILO config + * at boot time or in lilo.conf. I have the + * following in my lilo.conf: + * + * append="cdu31a=0x1f88,0,PAS" + * + * The first number is the I/O base address of the + * card. The second is the interrupt (0 means none). + * The third should be "PAS" if on a Pro-Audio + * spectrum, or nothing if on something else. + * + * This interface is (unfortunately) a polled interface. This is + * because most Sony interfaces are set up with DMA and interrupts + * disables. Some (like mine) do not even have the capability to + * handle interrupts or DMA. For this reason you will see a lot of + * the following: + * + * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT; + * while ((retry_count > jiffies) && (! +#include + +#ifdef MODULE +# include +# include +char kernel_version[]= UTS_RELEASE; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define MAJOR_NR CDU31A_CDROM_MAJOR +#include + +#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 readahead_dataleft) + { + /* The readahead will not fill the requested buffer, but + get the data out of the readahead into the buffer. */ + memcpy(buffer, + readahead_buffer + (2048 - readahead_dataleft), + readahead_dataleft); + readahead_dataleft = 0; + bytesleft -= readahead_dataleft; + offset += readahead_dataleft; + } + else + { + /* The readahead will fill the whole buffer, get the data + and return. */ + memcpy(buffer, + readahead_buffer + (2048 - readahead_dataleft), + bytesleft); + readahead_dataleft -= bytesleft; + bytesleft = 0; + sony_blocks_left -= nblocks; + sony_next_block += nblocks; + + /* If the data in the readahead is bad, return an error so the + driver will abort the buffer. */ + if (readahead_bad) + { + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + } + + if (readahead_dataleft == 0) + { + readahead_bad = 0; + } + + /* Final transfer is done for read command, get final result. */ + if (sony_blocks_left == 0) + { + get_result(res_reg, res_size); + } + return; + } + } + + /* Wait for the drive to tell us we have something */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && !(is_data_ready())) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + if (!(is_data_ready())) + { + if (is_result_ready()) + { + get_result(res_reg, res_size); + if ((res_reg[0] & 0xf0) != 0x20) + { + printk("CDU31A: Got result that should have been error: %d\n", + res_reg[0]); + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + } + abort_read(); + } + else + { +#if DEBUG + printk("CDU31A timeout out %d\n", __LINE__); +#endif + res_reg[0] = 0x20; + res_reg[1] = SONY_TIMEOUT_OP_ERR; + *res_size = 2; + abort_read(); + } + } + else + { + input_data(buffer, bytesleft, nblocks, offset, skip); + + /* Wait for the status from the drive. */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && !(is_result_ready())) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + + if (!is_result_ready()) + { +#if DEBUG + printk("CDU31A timeout out %d\n", __LINE__); +#endif + res_reg[0] = 0x20; + res_reg[1] = SONY_TIMEOUT_OP_ERR; + *res_size = 2; + abort_read(); + } + else + { + get_result(res_reg, res_size); + + /* If we got a buffer status, handle that. */ + if ((res_reg[0] & 0xf0) == 0x50) + { + + if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT) + || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT) + || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT)) + { + /* The data was successful, but if data was read from + the readahead and it was bad, set the whole + buffer as bad. */ + if (readahead_bad) + { + readahead_bad = 0; + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + } + } + else + { + printk("CDU31A: Data block error: 0x%x\n", res_reg[0]); + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + + /* Data is in the readahead buffer but an error was returned. + Make sure future requests don't use the data. */ + if (bytesleft != 2048) + { + readahead_bad = 1; + } + } + + /* Final transfer is done for read command, get final result. */ + if (sony_blocks_left == 0) + { + get_result(res_reg, res_size); + } + } + else if ((res_reg[0] & 0xf0) != 0x20) + { + /* The drive gave me bad status, I don't know what to do. + Reset the driver and return an error. */ + printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]); + restart_on_error(); + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + } + } + } +} + +/* + * The OS calls this to perform a read or write operation to the drive. + * Write obviously fail. Reads to a read ahead of sony_buffer_size + * bytes to help speed operations. This especially helps since the OS + * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most + * data access on a CD is done sequentially, this saves a lot of operations. + */ +static void +do_cdu31a_request(void) +{ + int block; + int nblock; + unsigned char res_reg[12]; + unsigned int res_size; + int num_retries; + unsigned long flags; + + + /* + * Make sure no one else is using the driver; wait for them + * to finish if it is so. + */ + save_flags(flags); + cli(); + while (sony_inuse) + { + interruptible_sleep_on(&sony_wait); + if (current->signal & ~current->blocked) + { + restore_flags(flags); + if (CURRENT && CURRENT->rq_status != RQ_INACTIVE) + { + end_request(0); + } + restore_flags(flags); + return; + } + } + sony_inuse = 1; + has_cd_task = current; + + /* Get drive status before doing anything. */ + while (handle_sony_cd_attention()) + ; + + sti(); + + /* If the timer is running, cancel it. */ + if (cdu31a_abort_timer.next != NULL) + { + del_timer(&cdu31a_abort_timer); + } + + while (1) + { +cdu31a_request_startover: + /* + * The beginning here is stolen from the hard disk driver. I hope + * it's right. + */ + if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) + { + goto end_do_cdu31a_request; + } + + if (!sony_spun_up) + { + struct inode in; + + /* This is a kludge to get a valid dev in an inode that + scd_open can take. That's the only thing scd_open() + uses the inode for. */ + in.i_rdev = CURRENT->rq_dev; + scd_open(&in,NULL); + } + + /* I don't use INIT_REQUEST because it calls return, which would + return without unlocking the device. It shouldn't matter, + but just to be safe... */ + if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) + { + panic(DEVICE_NAME ": request list destroyed"); + } + if (CURRENT->bh) + { + if (!CURRENT->bh->b_lock) + { + panic(DEVICE_NAME ": block not locked"); + } + } + + block = CURRENT->sector; + nblock = CURRENT->nr_sectors; + + if (!sony_toc_read) + { + printk("CDU31A: TOC not read\n"); + end_request(0); + goto cdu31a_request_startover; + } + + /* Check for base read of multi-session disk. This will still work + for single session disks, so just do it. Blocks less than 80 + are for the volume info, so offset them by the start track (which + should be zero for a single-session disk). */ + if (block < 80) + { + /* Offset the request into the session. */ + block += (sony_toc.start_track_lba * 4); + } + + switch(CURRENT->cmd) + { + case READ: + /* + * If the block address is invalid or the request goes beyond the end of + * the media, return an error. + */ +#if 0 + if ((block / 4) < sony_toc.start_track_lba) + { + printk("CDU31A: Request before beginning of media\n"); + end_request(0); + goto cdu31a_request_startover; + } +#endif + if ((block / 4) >= sony_toc.lead_out_start_lba) + { + printk("CDU31A: Request past end of media\n"); + end_request(0); + goto cdu31a_request_startover; + } + if (((block + nblock) / 4) >= sony_toc.lead_out_start_lba) + { + printk("CDU31A: Request past end of media\n"); + end_request(0); + goto cdu31a_request_startover; + } + + num_retries = 0; + +try_read_again: + while (handle_sony_cd_attention()) + ; + + if (!sony_toc_read) + { + printk("CDU31A: TOC not read\n"); + end_request(0); + goto cdu31a_request_startover; + } + + /* If no data is left to be read from the drive, start the + next request. */ + if (sony_blocks_left == 0) + { + if (start_request(block / 4, CDU31A_READAHEAD / 4, 0)) + { + end_request(0); + goto cdu31a_request_startover; + } + } + /* If the requested block is not the next one waiting in + the driver, abort the current operation and start a + new one. */ + else if (block != sony_next_block) + { +#if DEBUG + printk("CDU31A Warning: Read for block %d, expected %d\n", + block, + sony_next_block); +#endif + abort_read(); + if (!sony_toc_read) + { + printk("CDU31A: TOC not read\n"); + end_request(0); + goto cdu31a_request_startover; + } + if (start_request(block / 4, CDU31A_READAHEAD / 4, 0)) + { + printk("CDU31a: start request failed\n"); + end_request(0); + goto cdu31a_request_startover; + } + } + + read_data_block(CURRENT->buffer, block, nblock, res_reg, &res_size); + if (res_reg[0] == 0x20) + { + if (num_retries > MAX_CDU31A_RETRIES) + { + end_request(0); + goto cdu31a_request_startover; + } + + num_retries++; + if (res_reg[1] == SONY_NOT_SPIN_ERR) + { + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + } + else + { + printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]); + } + goto try_read_again; + } + else + { + end_request(1); + } + break; + + case WRITE: + end_request(0); + break; + + default: + panic("CDU31A: Unknown cmd"); + } + } + +end_do_cdu31a_request: +#if 0 + /* After finished, cancel any pending operations. */ + abort_read(); +#else + /* Start a timer to time out after a while to disable + the read. */ + cdu31a_abort_timer.expires = jiffies + 2*HZ; /* Wait 2 seconds */ + add_timer(&cdu31a_abort_timer); +#endif + + has_cd_task = NULL; + sony_inuse = 0; + wake_up_interruptible(&sony_wait); + restore_flags(flags); +} + +/* Copy overlapping buffers. */ +static void +mcovlp(char *dst, + char *src, + int size) +{ + src += (size - 1); + dst += (size - 1); + while (size > 0) + { + *dst = *src; + size--; + dst--; + src--; + } +} + + +/* + * Read the table of contents from the drive and set up TOC if + * successful. + */ +static void +sony_get_toc(void) +{ + unsigned char res_reg[2]; + unsigned int res_size; + unsigned char parms[1]; + int session; + + +#if DEBUG + printk("Entering sony_get_toc\n"); +#endif + + if (!sony_toc_read) + { + /* The idea here is we keep asking for sessions until the command + fails. Then we know what the last valid session on the disk is. + No need to check session 0, since session 0 is the same as session + 1; the command returns different information if you give it 0. + Don't check session 1 because that is the first session, it must + be there. */ + session = 2; + while (1) + { +#if DEBUG + printk("Trying session %d\n", session); +#endif + parms[0] = session; + do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD, + parms, + 1, + res_reg, + &res_size); + +#if DEBUG + printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]); +#endif + + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + /* An error reading the TOC, this must be past the last session. */ + break; + } + + session++; + + /* Let's not get carried away... */ + if (session > 20) + { + return; + } + } + + session--; + +#if DEBUG + printk("Reading session %d\n", session); +#endif + + parms[0] = session; + do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD, + parms, + 1, + (unsigned char *) &sony_toc, + &res_size); + if ((res_size < 2) || ((sony_toc.exec_status[0] & 0xf0) == 0x20)) + { + /* An error reading the TOC. Return without sony_toc_read + set. */ + return; + } + + sony_toc_read = 1; + + /* For points that do not exist, move the data over them + to the right location. */ + if (sony_toc.pointb0 != 0xb0) + { + mcovlp(((char *) &sony_toc) + 27, + ((char *) &sony_toc) + 18, + res_size - 18); + res_size += 9; + } + if (sony_toc.pointb1 != 0xb1) + { + mcovlp(((char *) &sony_toc) + 36, + ((char *) &sony_toc) + 27, + res_size - 27); + res_size += 9; + } + if (sony_toc.pointb2 != 0xb2) + { + mcovlp(((char *) &sony_toc) + 45, + ((char *) &sony_toc) + 36, + res_size - 36); + res_size += 9; + } + if (sony_toc.pointb3 != 0xb3) + { + mcovlp(((char *) &sony_toc) + 54, + ((char *) &sony_toc) + 45, + res_size - 45); + res_size += 9; + } + if (sony_toc.pointb4 != 0xb4) + { + mcovlp(((char *) &sony_toc) + 63, + ((char *) &sony_toc) + 54, + res_size - 54); + res_size += 9; + } + if (sony_toc.pointc0 != 0xc0) + { + mcovlp(((char *) &sony_toc) + 72, + ((char *) &sony_toc) + 63, + res_size - 63); + res_size += 9; + } + + sony_toc.start_track_lba = msf_to_log(sony_toc.tracks[0].track_start_msf); + sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf); + +#if DEBUG + printk("Disk session %d, start track: %d, stop track: %d\n", + session, + sony_toc.start_track_lba, + sony_toc.lead_out_start_lba); +#endif + } +#if DEBUG + printk("Leaving sony_get_toc\n"); +#endif +} + + +/* + * Search for a specific track in the table of contents. + */ +static int +find_track(int track) +{ + int i; + int num_tracks; + + + num_tracks = sony_toc.last_track_num - sony_toc.first_track_num + 1; + for (i = 0; i < num_tracks; i++) + { + if (sony_toc.tracks[i].track == track) + { + return i; + } + } + + return -1; +} + + +/* + * Read the subcode and put it int last_sony_subcode for future use. + */ +static int +read_subcode(void) +{ + unsigned int res_size; + + + do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD, + NULL, + 0, + (unsigned char *) &last_sony_subcode, + &res_size); + if ((res_size < 2) || ((last_sony_subcode.exec_status[0] & 0xf0) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (read_subcode)\n", + last_sony_subcode.exec_status[1]); + return -EIO; + } + + return 0; +} + + +/* + * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If + * the drive is playing, the subchannel needs to be read (since it would be + * changing). If the drive is paused or completed, the subcode information has + * already been stored, just use that. The ioctl call wants things in decimal + * (not BCD), so all the conversions are done. + */ +static int +sony_get_subchnl_info(long arg) +{ + struct cdrom_subchnl schi; + + + /* Get attention stuff */ + while (handle_sony_cd_attention()) + ; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + verify_area(VERIFY_READ, (char *) arg, sizeof(schi)); + verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi)); + + memcpy_fromfs(&schi, (char *) arg, sizeof(schi)); + + switch (sony_audio_status) + { + case CDROM_AUDIO_PLAY: + if (read_subcode() < 0) + { + return -EIO; + } + break; + + case CDROM_AUDIO_PAUSED: + case CDROM_AUDIO_COMPLETED: + break; + + case CDROM_AUDIO_NO_STATUS: + schi.cdsc_audiostatus = sony_audio_status; + memcpy_tofs((char *) arg, &schi, sizeof(schi)); + return 0; + break; + + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_ERROR: + default: + return -EIO; + } + + schi.cdsc_audiostatus = sony_audio_status; + schi.cdsc_adr = last_sony_subcode.address; + schi.cdsc_ctrl = last_sony_subcode.control; + schi.cdsc_trk = bcd_to_int(last_sony_subcode.track_num); + schi.cdsc_ind = bcd_to_int(last_sony_subcode.index_num); + if (schi.cdsc_format == CDROM_MSF) + { + schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode.abs_msf[0]); + schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode.abs_msf[1]); + schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode.abs_msf[2]); + + schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode.rel_msf[0]); + schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode.rel_msf[1]); + schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode.rel_msf[2]); + } + else if (schi.cdsc_format == CDROM_LBA) + { + schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf); + schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf); + } + + memcpy_tofs((char *) arg, &schi, sizeof(schi)); + return 0; +} + +/* Get audio data from the drive. This is fairly complex because I + am looking for status and data at the same time, but if I get status + then I just look for data. I need to get the status immediately so + the switch from audio to data tracks will happen quickly. */ +static void +read_audio_data(char *buffer, + unsigned char res_reg[], + int *res_size) +{ + unsigned int retry_count; + int result_read; + + + res_reg[0] = 0; + res_reg[1] = 0; + *res_size = 0; + result_read = 0; + + /* Wait for the drive to tell us we have something */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; +continue_read_audio_wait: + while ( (retry_count > jiffies) + && !(is_data_ready()) + && !(is_result_ready() || result_read)) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + if (!(is_data_ready())) + { + if (is_result_ready() && !result_read) + { + get_result(res_reg, res_size); + + /* Read block status and continue waiting for data. */ + if ((res_reg[0] & 0xf0) == 0x50) + { + result_read = 1; + goto continue_read_audio_wait; + } + /* Invalid data from the drive. Shut down the operation. */ + else if ((res_reg[0] & 0xf0) != 0x20) + { + printk("CDU31A: Got result that should have been error: %d\n", + res_reg[0]); + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + } + abort_read(); + } + else + { +#if DEBUG + printk("CDU31A timeout out %d\n", __LINE__); +#endif + res_reg[0] = 0x20; + res_reg[1] = SONY_TIMEOUT_OP_ERR; + *res_size = 2; + abort_read(); + } + } + else + { + clear_data_ready(); + + /* If data block, then get 2340 bytes offset by 12. */ + if (sony_raw_data_mode) + { + insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_XA); + } + else + { + /* Audio gets the whole 2352 bytes. */ + insb(sony_cd_read_reg, buffer, CD_FRAMESIZE_RAW); + } + + /* If I haven't already gotten the result, get it now. */ + if (!result_read) + { + /* Wait for the drive to tell us we have something */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && !(is_result_ready())) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + + if (!is_result_ready()) + { +#if DEBUG + printk("CDU31A timeout out %d\n", __LINE__); +#endif + res_reg[0] = 0x20; + res_reg[1] = SONY_TIMEOUT_OP_ERR; + *res_size = 2; + abort_read(); + return; + } + else + { + get_result(res_reg, res_size); + } + } + + if ((res_reg[0] & 0xf0) == 0x50) + { + if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT) + || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT) + || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT) + || (res_reg[0] == SONY_NO_ERR_DETECTION_STAT)) + { + /* Ok, nothing to do. */ + } + else + { + printk("CDU31A: Data block error: 0x%x\n", res_reg[0]); + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + } + } + else if ((res_reg[0] & 0xf0) != 0x20) + { + /* The drive gave me bad status, I don't know what to do. + Reset the driver and return an error. */ + printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]); + restart_on_error(); + res_reg[0] = 0x20; + res_reg[1] = SONY_BAD_DATA_ERR; + *res_size = 2; + } + } +} + +/* Perform a raw data read. This will automatically detect the + track type and read the proper data (audio or data). */ +static int +read_audio(struct cdrom_read_audio *ra, + struct inode *inode) +{ + int retval; + unsigned char params[2]; + unsigned char res_reg[12]; + unsigned int res_size; + unsigned int cframe; + unsigned long flags; + + /* + * Make sure no one else is using the driver; wait for them + * to finish if it is so. + */ + save_flags(flags); + cli(); + while (sony_inuse) + { + interruptible_sleep_on(&sony_wait); + if (current->signal & ~current->blocked) + { + restore_flags(flags); + return -EAGAIN; + } + } + sony_inuse = 1; + has_cd_task = current; + restore_flags(flags); + + if (!sony_spun_up) + { + scd_open (inode, NULL); + } + + /* Set the drive to do raw operations. */ + params[0] = SONY_SD_DECODE_PARAM; + params[1] = 0x06 | sony_raw_data_mode; + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, + params, + 2, + res_reg, + &res_size); + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]); + return -EIO; + } + + /* From here down, we have to goto exit_read_audio instead of returning + because the drive parameters have to be set back to data before + return. */ + + retval = 0; + /* start_request clears out any readahead data, so it should be safe. */ + if (start_request(ra->addr.lba, ra->nframes, 1)) + { + retval = -EIO; + goto exit_read_audio; + } + + /* For every requested frame. */ + cframe = 0; + while (cframe < ra->nframes) + { + read_audio_data(readahead_buffer, res_reg, &res_size); + if ((res_reg[0] & 0xf0) == 0x20) + { + if (res_reg[1] == SONY_BAD_DATA_ERR) + { + printk("CDU31A: Data error on audio sector %d\n", + ra->addr.lba + cframe); + } + else if (res_reg[1] == SONY_ILL_TRACK_R_ERR) + { + /* Illegal track type, change track types and start over. */ + sony_raw_data_mode = (sony_raw_data_mode) ? 0 : 1; + + /* Set the drive mode. */ + params[0] = SONY_SD_DECODE_PARAM; + params[1] = 0x06 | sony_raw_data_mode; + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, + params, + 2, + res_reg, + &res_size); + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]); + retval = -EIO; + goto exit_read_audio; + } + + /* Restart the request on the current frame. */ + if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1)) + { + retval = -EIO; + goto exit_read_audio; + } + + /* Don't go back to the top because don't want to get into + and infinite loop. A lot of code gets duplicated, but + that's no big deal, I don't guess. */ + read_audio_data(readahead_buffer, res_reg, &res_size); + if ((res_reg[0] & 0xf0) == 0x20) + { + if (res_reg[1] == SONY_BAD_DATA_ERR) + { + printk("CDU31A: Data error on audio sector %d\n", + ra->addr.lba + cframe); + } + else + { + printk("CDU31A: Error reading audio data on sector %d: 0x%x\n", + ra->addr.lba + cframe, + res_reg[1]); + retval = -EIO; + goto exit_read_audio; + } + } + else + { + memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)), + (char *) readahead_buffer, + CD_FRAMESIZE_RAW); + } + } + else + { + printk("CDU31A: Error reading audio data on sector %d: 0x%x\n", + ra->addr.lba + cframe, + res_reg[1]); + retval = -EIO; + goto exit_read_audio; + } + } + else + { + memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)), + (char *) readahead_buffer, + CD_FRAMESIZE_RAW); + } + + cframe++; + } + + get_result(res_reg, &res_size); + if ((res_reg[0] & 0xf0) == 0x20) + { + printk("CDU31A: Error return from audio read: 0x%x\n", + res_reg[1]); + retval = -EIO; + goto exit_read_audio; + } + +exit_read_audio: + + /* Set the drive mode back to the proper one for the disk. */ + params[0] = SONY_SD_DECODE_PARAM; + if (!sony_xa_mode) + { + params[1] = 0x0f; + } + else + { + params[1] = 0x07; + } + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, + params, + 2, + res_reg, + &res_size); + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + printk("CDU31A: Unable to reset decode params: 0x%2.2x\n", res_reg[1]); + return -EIO; + } + + has_cd_task = NULL; + sony_inuse = 0; + wake_up_interruptible(&sony_wait); + + return(retval); +} + +static int +do_sony_cd_cmd_chk(const char *name, + unsigned char cmd, + unsigned char *params, + unsigned int num_params, + unsigned char *result_buffer, + unsigned int *result_size) +{ + do_sony_cd_cmd(cmd, params, num_params, result_buffer, result_size); + if ((*result_size < 2) || ((result_buffer[0] & 0xf0) == 0x20)) + { + printk("Sony CDROM error 0x%2.2x (CDROM%s)\n", result_buffer[1], name); + return -EIO; + } + return 0; +} + +/* + * The big ugly ioctl handler. + */ +static int scd_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned char res_reg[12]; + unsigned int res_size; + unsigned char params[7]; + int i; + + + if (!inode) + { + return -EINVAL; + } + + switch (cmd) + { + case CDROMSTART: /* Spin up the drive */ + return do_sony_cd_cmd_chk("START",SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + return 0; + break; + + case CDROMSTOP: /* Spin down the drive */ + do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); + + /* + * Spin the drive down, ignoring the error if the disk was + * already not spinning. + */ + sony_audio_status = CDROM_AUDIO_NO_STATUS; + return do_sony_cd_cmd_chk("STOP",SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + case CDROMPAUSE: /* Pause the drive */ + if(do_sony_cd_cmd_chk("PAUSE", SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size)) + return -EIO; + /* Get the current position and save it for resuming */ + if (read_subcode() < 0) + { + return -EIO; + } + cur_pos_msf[0] = last_sony_subcode.abs_msf[0]; + cur_pos_msf[1] = last_sony_subcode.abs_msf[1]; + cur_pos_msf[2] = last_sony_subcode.abs_msf[2]; + sony_audio_status = CDROM_AUDIO_PAUSED; + return 0; + break; + + case CDROMRESUME: /* Start the drive after being paused */ + if (sony_audio_status != CDROM_AUDIO_PAUSED) + { + return -EINVAL; + } + + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + + /* Start the drive at the saved position. */ + params[1] = cur_pos_msf[0]; + params[2] = cur_pos_msf[1]; + params[3] = cur_pos_msf[2]; + params[4] = final_pos_msf[0]; + params[5] = final_pos_msf[1]; + params[6] = final_pos_msf[2]; + params[0] = 0x03; + if(do_sony_cd_cmd_chk("RESUME",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0) + return -EIO; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + + case CDROMPLAYMSF: /* Play starting at the given MSF address. */ + i=verify_area(VERIFY_READ, (char *) arg, 6); + if(i) + return i; + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + memcpy_fromfs(&(params[1]), (void *) arg, 6); + + /* The parameters are given in int, must be converted */ + for (i=1; i<7; i++) + { + params[i] = int_to_bcd(params[i]); + } + params[0] = 0x03; + if(do_sony_cd_cmd_chk("PLAYMSF",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0) + return -EIO; + + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = params[4]; + final_pos_msf[1] = params[5]; + final_pos_msf[2] = params[6]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + + case CDROMREADTOCHDR: /* Read the table of contents header */ + { + struct cdrom_tochdr *hdr; + struct cdrom_tochdr loc_hdr; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + hdr = (struct cdrom_tochdr *) arg; + i=verify_area(VERIFY_WRITE, hdr, sizeof(*hdr)); + if(i<0) + return i; + loc_hdr.cdth_trk0 = bcd_to_int(sony_toc.first_track_num); + loc_hdr.cdth_trk1 = bcd_to_int(sony_toc.last_track_num); + memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr)); + } + return 0; + + case CDROMREADTOCENTRY: /* Read a given table of contents entry */ + { + struct cdrom_tocentry *entry; + struct cdrom_tocentry loc_entry; + int track_idx; + unsigned char *msf_val = NULL; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + entry = (struct cdrom_tocentry *) arg; + i=verify_area(VERIFY_READ, entry, sizeof(*entry)); + if(i<0) + return i; + i=verify_area(VERIFY_WRITE, entry, sizeof(*entry)); + if(i<0) + return i; + + memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry)); + + /* Lead out is handled separately since it is special. */ + if (loc_entry.cdte_track == CDROM_LEADOUT) + { + loc_entry.cdte_adr = sony_toc.address2; + loc_entry.cdte_ctrl = sony_toc.control2; + msf_val = sony_toc.lead_out_start_msf; + } + else + { + track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); + if (track_idx < 0) + { + return -EINVAL; + } + + loc_entry.cdte_adr = sony_toc.tracks[track_idx].address; + loc_entry.cdte_ctrl = sony_toc.tracks[track_idx].control; + msf_val = sony_toc.tracks[track_idx].track_start_msf; + } + + /* Logical buffer address or MSF format requested? */ + if (loc_entry.cdte_format == CDROM_LBA) + { + loc_entry.cdte_addr.lba = msf_to_log(msf_val); + } + else if (loc_entry.cdte_format == CDROM_MSF) + { + loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); + loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1)); + loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2)); + } + memcpy_tofs(entry, &loc_entry, sizeof(*entry)); + } + return 0; + break; + + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + { + struct cdrom_ti ti; + int track_idx; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + i=verify_area(VERIFY_READ, (char *) arg, sizeof(ti)); + if(i<0) + return i; + + memcpy_fromfs(&ti, (char *) arg, sizeof(ti)); + if ( (ti.cdti_trk0 < sony_toc.first_track_num) + || (ti.cdti_trk0 > sony_toc.last_track_num) + || (ti.cdti_trk1 < ti.cdti_trk0)) + { + return -EINVAL; + } + + track_idx = find_track(int_to_bcd(ti.cdti_trk0)); + if (track_idx < 0) + { + return -EINVAL; + } + params[1] = sony_toc.tracks[track_idx].track_start_msf[0]; + params[2] = sony_toc.tracks[track_idx].track_start_msf[1]; + params[3] = sony_toc.tracks[track_idx].track_start_msf[2]; + + /* + * If we want to stop after the last track, use the lead-out + * MSF to do that. + */ + if (ti.cdti_trk1 >= bcd_to_int(sony_toc.last_track_num)) + { + log_to_msf(msf_to_log(sony_toc.lead_out_start_msf)-1, + &(params[4])); + } + else + { + track_idx = find_track(int_to_bcd(ti.cdti_trk1+1)); + if (track_idx < 0) + { + return -EINVAL; + } + log_to_msf(msf_to_log(sony_toc.tracks[track_idx].track_start_msf)-1, + &(params[4])); + } + params[0] = 0x03; + + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + + do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size); + + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1], + params[2], params[3], params[4], params[5], params[6]); + printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]); + return -EIO; + } + + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = params[4]; + final_pos_msf[1] = params[5]; + final_pos_msf[2] = params[6]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + } + + case CDROMSUBCHNL: /* Get subchannel info */ + return sony_get_subchnl_info(arg); + + case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ + { + struct cdrom_volctrl volctrl; + + i=verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl)); + if(i<0) + return i; + + memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl)); + params[0] = SONY_SD_AUDIO_VOLUME; + params[1] = volctrl.channel0; + params[2] = volctrl.channel1; + return do_sony_cd_cmd_chk("VOLCTRL",SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size); + } + case CDROMEJECT: /* Eject the drive */ + do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + sony_audio_status = CDROM_AUDIO_INVALID; + return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size); + + case CDROMREADAUDIO: /* Read 2352 byte audio tracks and 2340 byte + raw data tracks. */ + { + struct cdrom_read_audio ra; + + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + i=verify_area(VERIFY_READ, (char *) arg, sizeof(ra)); + if(i<0) + return i; + memcpy_fromfs(&ra, (char *) arg, sizeof(ra)); + + i=verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes); + if(i<0) + return i; + + if (ra.addr_format == CDROM_LBA) + { + if ( (ra.addr.lba >= sony_toc.lead_out_start_lba) + || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba)) + { + return -EINVAL; + } + } + else if (ra.addr_format == CDROM_MSF) + { + if ( (ra.addr.msf.minute >= 75) + || (ra.addr.msf.second >= 60) + || (ra.addr.msf.frame >= 75)) + { + return -EINVAL; + } + + ra.addr.lba = ( (ra.addr.msf.minute * 4500) + + (ra.addr.msf.second * 75) + + ra.addr.msf.frame); + if ( (ra.addr.lba >= sony_toc.lead_out_start_lba) + || (ra.addr.lba + ra.nframes >= sony_toc.lead_out_start_lba)) + { + return -EINVAL; + } + + /* I know, this can go negative on an unsigned. However, + the first thing done to the data is to add this value, + so this should compensate and allow direct msf access. */ + ra.addr.lba -= LOG_START_OFFSET; + } + else + { + return -EINVAL; + } + + return(read_audio(&ra, inode)); + } + return 0; + break; + + default: + return -EINVAL; + } +} + + +/* + * Open the drive for operations. Spin the drive up and read the table of + * contents if these have not already been done. + */ +static int +scd_open(struct inode *inode, + struct file *filp) +{ + unsigned char res_reg[12]; + unsigned int res_size; + int num_spin_ups; + unsigned char params[2]; + + + if ((filp) && filp->f_mode & 2) + return -EROFS; + + if (!sony_spun_up) + { + num_spin_ups = 0; + +respinup_on_open: + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + + /* The drive sometimes returns error 0. I don't know why, but ignore + it. It seems to mean the drive has already done the operation. */ + if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) + { + printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]); + return -EIO; + } + + do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); + + /* The drive sometimes returns error 0. I don't know why, but ignore + it. It seems to mean the drive has already done the operation. */ + if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) + { + /* If the drive is already playing, it's ok. */ + if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0)) + { + goto drive_spinning; + } + + /* If the drive says it is not spun up (even though we just did it!) + then retry the operation at least a few times. */ + if ( (res_reg[1] == SONY_NOT_SPIN_ERR) + && (num_spin_ups < MAX_CDU31A_RETRIES)) + { + num_spin_ups++; + goto respinup_on_open; + } + + printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + return -EIO; + } + + sony_get_toc(); + if (!sony_toc_read) + { + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + return -EIO; + } + + /* For XA on the CDU31A only, we have to do special reads. + The CDU33A handles XA automagically. */ + if ( (sony_toc.disk_type == SONY_XA_DISK_TYPE) + && (!is_double_speed)) + { + params[0] = SONY_SD_DECODE_PARAM; + params[1] = 0x07; + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, + params, + 2, + res_reg, + &res_size); + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]); + } + sony_xa_mode = 1; + } + /* A non-XA disk. Set the parms back if necessary. */ + else if (sony_xa_mode) + { + params[0] = SONY_SD_DECODE_PARAM; + params[1] = 0x0f; + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, + params, + 2, + res_reg, + &res_size); + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]); + } + sony_xa_mode = 0; + } + + sony_spun_up = 1; + } + +drive_spinning: + + /* If filp is not NULL (standard open), try a disk change. */ + if (filp) + { + check_disk_change(inode->i_rdev); + } + + sony_usage++; + MOD_INC_USE_COUNT; + + return 0; +} + + +/* + * Close the drive. Spin it down if no task is using it. The spin + * down will fail if playing audio, so audio play is OK. + */ +static void +scd_release(struct inode *inode, + struct file *filp) +{ + unsigned char res_reg[12]; + unsigned int res_size; + + + if (sony_usage > 0) + { + sony_usage--; + MOD_DEC_USE_COUNT; + } + if (sony_usage == 0) + { + sync_dev(inode->i_rdev); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + sony_spun_up = 0; + } +} + + +static struct file_operations scd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + scd_ioctl, /* ioctl */ + NULL, /* mmap */ + scd_open, /* open */ + scd_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + scd_disk_change, /* media_change */ + NULL /* revalidate */ +}; + + +/* The different types of disc loading mechanisms supported */ +static const char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" }; + +static void +get_drive_configuration(unsigned short base_io, + unsigned char res_reg[], + unsigned int *res_size) +{ + int retry_count; + + + /* Set the base address */ + cdu31a_port = base_io; + + /* Set up all the register locations */ + sony_cd_cmd_reg = cdu31a_port + SONY_CMD_REG_OFFSET; + sony_cd_param_reg = cdu31a_port + SONY_PARAM_REG_OFFSET; + sony_cd_write_reg = cdu31a_port + SONY_WRITE_REG_OFFSET; + sony_cd_control_reg = cdu31a_port + SONY_CONTROL_REG_OFFSET; + sony_cd_status_reg = cdu31a_port + SONY_STATUS_REG_OFFSET; + sony_cd_result_reg = cdu31a_port + SONY_RESULT_REG_OFFSET; + sony_cd_read_reg = cdu31a_port + SONY_READ_REG_OFFSET; + sony_cd_fifost_reg = cdu31a_port + SONY_FIFOST_REG_OFFSET; + + /* + * Check to see if anything exists at the status register location. + * I don't know if this is a good way to check, but it seems to work + * ok for me. + */ + if (read_status_register() != 0xff) + { + /* + * Reset the drive and wait for attention from it (to say it's reset). + * If you don't wait, the next operation will probably fail. + */ + reset_drive(); + retry_count = jiffies + SONY_RESET_TIMEOUT; + while ((retry_count > jiffies) && (!is_attention())) + { + sony_sleep(); + } + +#if 0 + /* If attention is never seen probably not a CDU31a present */ + if (!is_attention()) + { + res_reg[0] = 0x20; + return; + } +#endif + + /* + * Get the drive configuration. + */ + do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD, + NULL, + 0, + (unsigned char *) res_reg, + res_size); + return; + } + + /* Return an error */ + res_reg[0] = 0x20; +} + +#ifndef MODULE +/* + * Set up base I/O and interrupts, called from main.c. + */ +void +cdu31a_setup(char *strings, + int *ints) +{ + if (ints[0] > 0) + { + cdu31a_port = ints[1]; + } + if (ints[0] > 1) + { + cdu31a_irq = ints[2]; + } + if ((strings != NULL) && (*strings != '\0')) + { + if (strcmp(strings, "PAS") == 0) + { + sony_pas_init = 1; + } + else + { + printk("CDU31A: Unknown interface type: %s\n", strings); + } + } +} +#endif + +static int cdu31a_block_size; + +/* + * Initialize the driver. + */ +#ifdef MODULE +#define cdu31a_init init_module +#endif + +int +cdu31a_init(void) +{ + struct s_sony_drive_config drive_config; + unsigned int res_size; + int i; + int drive_found; + int tmp_irq; + + + /* + * According to Alex Freed (freed@europa.orion.adobe.com), this is + * required for the Fusion CD-16 package. If the sound driver is + * loaded, it should work fine, but just in case... + * + * The following turn on the CD-ROM interface for a Fusion CD-16. + */ + if (sony_pas_init) + { + outb(0xbc, 0x9a01); + outb(0xe2, 0x9a01); + } + + drive_found = 0; + + /* Setting the base I/O address to 0xffff will disable it. */ + if (cdu31a_port == 0xffff) + { + } + else if (cdu31a_port != 0) + { + tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */ + cdu31a_irq = 0; + + get_drive_configuration(cdu31a_port, + drive_config.exec_status, + &res_size); + if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00)) + { + drive_found = 1; + } + + cdu31a_irq = tmp_irq; + } + else + { + cdu31a_irq = 0; + i = 0; + while ( (cdu31a_addresses[i].base != 0) + && (!drive_found)) + { + if (check_region(cdu31a_addresses[i].base, 4)) { + i++; + continue; + } + get_drive_configuration(cdu31a_addresses[i].base, + drive_config.exec_status, + &res_size); + if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00)) + { + drive_found = 1; + cdu31a_irq = cdu31a_addresses[i].int_num; + } + else + { + i++; + } + } + } + + if (drive_found) + { + request_region(cdu31a_port, 4,"cdu31a"); + + if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops)) + { + printk("Unable to get major %d for CDU-31a\n", MAJOR_NR); + return -EIO; + } + + if (SONY_HWC_DOUBLE_SPEED(drive_config)) + { + is_double_speed = 1; + } + + tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */ + cdu31a_irq = 0; + + set_drive_params(); + + cdu31a_irq = tmp_irq; + + if (cdu31a_irq > 0) + { + if (request_irq(cdu31a_irq, cdu31a_interrupt, SA_INTERRUPT, "cdu31a")) + { + printk("Unable to grab IRQ%d for the CDU31A driver\n", cdu31a_irq); + cdu31a_irq = 0; + } + } + + printk("Sony I/F CDROM : %8.8s %16.16s %8.8s\n", + drive_config.vendor_id, + drive_config.product_id, + drive_config.product_rev_level); + printk(" Capabilities: %s", + load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]); + if (SONY_HWC_AUDIO_PLAYBACK(drive_config)) + { + printk(", audio"); + } + if (SONY_HWC_EJECT(drive_config)) + { + printk(", eject"); + } + if (SONY_HWC_LED_SUPPORT(drive_config)) + { + printk(", LED"); + } + if (SONY_HWC_ELECTRIC_VOLUME(drive_config)) + { + printk(", elec. Vol"); + } + if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config)) + { + printk(", sep. Vol"); + } + if (is_double_speed) + { + printk(", double speed"); + } + if (cdu31a_irq > 0) + { + printk(", irq %d", cdu31a_irq); + } + printk("\n"); + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = CDU31A_READAHEAD; + cdu31a_block_size = 1024; /* 1kB default block size */ + /* use 'mount -o block=2048' */ + blksize_size[MAJOR_NR] = &cdu31a_block_size; + + cdu31a_abort_timer.next = NULL; + cdu31a_abort_timer.prev = NULL; + cdu31a_abort_timer.function = handle_abort_timeout; + } + + + disk_changed = 1; + + if (drive_found) + { + return(0); + } + else + { + return -EIO; + } +} + +#ifdef MODULE +void +cleanup_module(void) +{ + if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) + { + printk("Can't unregister cdu31a\n"); + return; + } + release_region(cdu31a_port,4); + printk("cdu31a module released.\n"); +} +#endif MODULE diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c --- v1.3.35/linux/drivers/cdrom/cm206.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/cm206.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,1249 @@ +/* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card. + Copyright (c) 1995 David van Leeuwen. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +History: + Started 25 jan 1994. Waiting for documentation... + 22 feb 1995: 0.1a first reasonably safe polling driver. + Two major bugs, one in read_sector and one in + do_cm206_request, happened to cancel! + 25 feb 1995: 0.2a first reasonable interrupt driven version of above. + uart writes are still done in polling mode. + 25 feb 1995: 0.21a writes also in interrupt mode, still some + small bugs to be found... Larger buffer. + 2 mrt 1995: 0.22 Bug found (cd-> nowhere, interrupt was called in + initialization), read_ahead of 16. Timeouts implemented. + unclear if they do something... + 7 mrt 1995: 0.23 Start of background read-ahead. + 18 mrt 1995: 0.24 Working background read-ahead. (still problems) + 26 mrt 1995: 0.25 Multi-session ioctl added (kernel v1.2). + Statistics implemented, though separate stats206.h. + Accessible trough ioctl 0x1000 (just a number). + Hard to choose between v1.2 development and 1.1.75. + Bottom-half doesn't work with 1.2... + 0.25a: fixed... typo. Still problems... + 1 apr 1995: 0.26 Module support added. Most bugs found. Use kernel 1.2.n. + 5 apr 1995: 0.27 Auto-probe for the adapter card base address. + Auto-probe for the adaptor card irq line. + 7 apr 1995: 0.28 Added lilo setup support for base address and irq. + Use major number 32 (not in this source), officially + assigned to this driver. + 9 apr 1995: 0.29 Added very limited audio support. Toc_header, stop, pause, + resume, eject. Play_track ignores track info, because we can't + read a table-of-contents entry. Toc_entry is implemented + as a `placebo' function: always returns start of disc. + 3 may 1995: 0.30 Audio support completed. The get_toc_entry function + is implemented as a binary search. + 15 may 1995: 0.31 More work on audio stuff. Workman is not easy to + satisfy; changed binary search into linear search. + Auto-probe for base address somewhat relaxed. + 1 jun 1995: 0.32 Removed probe_irq_on/off for module version. + 10 jun 1995: 0.33 Workman still behaves funny, but you should be + able to eject and substitute another disc. + + An adaption of 0.33 is included in linux-1.3.7 by Eberhard Moenkeberg + + 18 jul 1996: 0.34 Patch by Heiko Eissfeldt included, mainly considering + verify_area's in the ioctls. Some bugs introduced by + EM considering the base port and irq fixed. + * + * Parts of the code are based upon lmscd.c written by Kai Petzke, + * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin + * Harriss, but any off-the-shelf dynamic programming algorithm won't + * be able to find them. + * + * The cm206 drive interface and the cm260 adapter card seem to be + * sufficiently different from their cm205/cm250 counterparts + * in order to write a complete new driver. + * + * I call all routines connected to the Linux kernel something + * with `cm206' in it, as this stuff is too series-dependent. + * + * Currently, my limited knowledge is based on: + * - The Linux Kernel Hacker's guide, v. 0.5 , by Michael J. Johnson + * - Linux Kernel Programmierung, by Michael Beck and others + * - Philips/LMS cm206 and cm226 product specification + * - Philips/LMS cm260 product specification + * + * David van Leeuwen, david@tm.tno.nl. */ +#define VERSION "0.34" + +#ifdef MODULE /* OK, so some of this is stolen */ +#include +#include +#ifndef CONFIG_MODVERSIONS +char kernel_version[]=UTS_RELEASE; +#endif +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif MODULE + +#include /* These include what we really need */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAJOR_NR CM206_CDROM_MAJOR +#include +#include + +/* 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 +#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; ibackground) 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; inr_sectors; i++) { + cd_sec_no = (CURRENT->sector+i)/4; /* 4 times 512 bytes */ + quarter = (CURRENT->sector+i) % 4; + dest = CURRENT->buffer + i*512; + /* is already in buffer memory? */ + if (cd->sector_first <= cd_sec_no && cd_sec_no < cd->sector_last) { + source = ((uch *) cd->sector) + 16 + + quarter*512 + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE; + memcpy(dest, source, 512); + } + else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) { + source = ((uch *) cd->sector)+16+quarter*512; + memcpy(dest, source, 512); + } + else { + error=1; + } + } + end_request(!error); + } +} + +int get_multi_session_info(struct cdrom_multisession * mssp) +{ + if (!FIRST_TRACK) get_disc_status(); + if (mssp) { + if (DISC_STATUS & cds_multi_session) { /* multi-session */ + if (mssp->addr_format == CDROM_LBA) + mssp->addr.lba = fsm2lba(&cd->disc_status[3]); + else { + mssp->addr.msf.frame = cd->disc_status[3]; + mssp->addr.msf.second = cd->disc_status[4]; + mssp->addr.msf.minute = cd->disc_status[5]; + } + mssp->xa_flag = 1; + } else { + mssp->xa_flag = 0; + } + return 1; + } + return 0; +} + +/* Audio support. I've tried very hard, but the cm206 drive doesn't + seem to have a get_toc (table-of-contents) function, while i'm + pretty sure it must read the toc upon disc insertion. Therefore + this function has been implemented through a binary search + strategy. All track starts that happen to be found are stored in + cd->toc[], for future use. + + I've spent a whole day on a bug that only shows under Workman--- + I don't get it. Tried everything, nothing works. If workman asks + for track# 0xaa, it'll get the wrong time back. Any other program + receives the correct value. I'm stymied. +*/ + +/* seek seeks to address lba. It does wait to arrive there. */ +void seek(int lba) +{ + int i; + uch seek_command[4]={c_seek, }; + + fsm(lba, &seek_command[1]); + for (i=0; i<4; i++) type_0_command(seek_command[i], 0); + cd->dsb = wait_dsb(); +} + +uch bcdbin(unsigned char bcd) /* stolen from mcd.c! */ +{ + return (bcd >> 4)*10 + (bcd & 0xf); +} + +inline uch normalize_track(uch track) +{ + if (track<1) return 1; + if (track>LAST_TRACK) return LAST_TRACK+1; + return track; +} + +/* This function does a binary search for track start. It records all + * tracks seen in the process. Input $track$ must be between 1 and + * #-of-tracks+1 */ +int get_toc_lba(uch track) +{ + int max=74*60*75-150, min=0; + int i, lba, l, old_lba=0; + uch * q = cd->q; + uch ct; /* current track */ + int binary=0; + const skip = 3*60*75; + + for (i=track; i>0; i--) if (cd->toc[i].track) { + min = fsm2lba(cd->toc[i].fsm); + break; + } + lba = min + skip; /* 3 minutes */ + do { + seek(lba); + type_1_command(c_read_current_q, 10, q); + ct = normalize_track(q[1]); + if (!cd->toc[ct].track) { + l = q[9]-bcdbin(q[5]) + 75*(q[8]-bcdbin(q[4])-2 + + 60*(q[7]-bcdbin(q[3]))); + cd->toc[ct].track=q[1]; /* lead out still 0xaa */ + fsm(l, cd->toc[ct].fsm); + cd->toc[ct].q0 = q[0]; /* contains adr and ctrl info */ +/* + if (ct==LAST_TRACK+1) + printk("Leadout %x %x %x %x %d %d %d \n", q[1], q[3], q[4], q[5], + q[7], q[8], q[9]); +*/ + if (ct==track) return l; + } + old_lba=lba; + if (binary) { + if (ct < track) min = lba; else max = lba; + lba = (min+max)/2; + } else { + if(ct < track) lba += skip; + else { + binary=1; + max = lba; min = lba - skip; + lba = (min+max)/2; + } + } + } while (lba!=old_lba); + return lba; +} + +void update_toc_entry(uch track) +{ + track = normalize_track(track); + if (!cd->toc[track].track) get_toc_lba(track); +} + +int read_toc_header(struct cdrom_tochdr * hp) +{ + if (!FIRST_TRACK) get_disc_status(); + if (hp && DISC_STATUS & cds_all_audio) { /* all audio */ + int i; + hp->cdth_trk0 = FIRST_TRACK; + hp->cdth_trk1 = LAST_TRACK; + cd->toc[1].track=1; /* fill in first track position */ + for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i]; + update_toc_entry(LAST_TRACK+1); /* find most entries */ + return 1; + } + return 0; +} + +void play_from_to_msf(struct cdrom_msf* msfp) +{ + uch play_command[] = {c_play, + msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0, + msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2, 2}; + int i; + for (i=0; i<9; i++) type_0_command(play_command[i], 0); + for (i=0; i<3; i++) + PLAY_TO.fsm[i] = play_command[i+4]; + PLAY_TO.track = 0; /* say no track end */ + cd->dsb = wait_dsb(); +} + +void play_from_to_track(int from, int to) +{ + uch play_command[8] = {c_play, }; + int i; + + if (from==0) { /* continue paused play */ + for (i=0; i<3; i++) { + play_command[i+1] = cd->audio_status[i+2]; + play_command[i+4] = PLAY_TO.fsm[i]; + } + } else { + update_toc_entry(from); update_toc_entry(to+1); + for (i=0; i<3; i++) { + play_command[i+1] = cd->toc[from].fsm[i]; + PLAY_TO.fsm[i] = play_command[i+4] = cd->toc[to+1].fsm[i]; + } + PLAY_TO.track = to; + } + for (i=0; i<7; i++) type_0_command(play_command[i],0); + for (i=0; i<2; i++) type_0_command(0x2, 0); /* volume */ + cd->dsb = wait_dsb(); +} + +int get_current_q(struct cdrom_subchnl * qp) +{ + int i; + uch * q = cd->q; + if (type_1_command(c_read_current_q, 10, q)) return 0; +/* q[0] = bcdbin(q[0]); Don't think so! */ + for (i=2; i<6; i++) q[i]=bcdbin(q[i]); + qp->cdsc_adr = q[0] & 0xf; qp->cdsc_ctrl = q[0] >> 4; /* from mcd.c */ + qp->cdsc_trk = q[1]; qp->cdsc_ind = q[2]; + if (qp->cdsc_format == CDROM_MSF) { + qp->cdsc_reladdr.msf.minute = q[3]; + qp->cdsc_reladdr.msf.second = q[4]; + qp->cdsc_reladdr.msf.frame = q[5]; + qp->cdsc_absaddr.msf.minute = q[7]; + qp->cdsc_absaddr.msf.second = q[8]; + qp->cdsc_absaddr.msf.frame = q[9]; + } else { + qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]); + qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]); + } + get_drive_status(); + if (cd->dsb & dsb_play_in_progress) + qp->cdsc_audiostatus = CDROM_AUDIO_PLAY ; + else if (PAUSED) + qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED; + else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; + return 1; +} + +void get_toc_entry(struct cdrom_tocentry * ep) +{ + uch track = normalize_track(ep->cdte_track); + update_toc_entry(track); + if (ep->cdte_format == CDROM_MSF) { + ep->cdte_addr.msf.frame = cd->toc[track].fsm[0]; + ep->cdte_addr.msf.second = cd->toc[track].fsm[1]; + ep->cdte_addr.msf.minute = cd->toc[track].fsm[2]; + } + else ep->cdte_addr.lba = fsm2lba(cd->toc[track].fsm); + ep->cdte_adr = cd->toc[track].q0 & 0xf; + ep->cdte_ctrl = cd->toc[track].q0 >> 4; + ep->cdte_datamode=0; +} + +/* Ioctl. I have made the statistics accessible through an ioctl + call. The constant is defined in cm206.h, it shouldn't clash with + the standard Linux ioctls. Multisession info is gathered at + run-time, this may turn out to be slow. */ + +static int cm206_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { +#ifdef STATISTICS + case CM206CTL_GET_STAT: + if (arg >= NR_STATS) return -EINVAL; + else return cd->stats[arg]; + case CM206CTL_GET_LAST_STAT: + if (arg >= NR_STATS) return -EINVAL; + else return cd->last_stat[arg]; +#endif + case CDROMMULTISESSION: { + struct cdrom_multisession ms_info; + int st; + stats(ioctl_multisession); + + st=verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct cdrom_multisession)); + if (st) return (st); + memcpy_fromfs(&ms_info, (struct cdrom_multisession *) arg, + sizeof(struct cdrom_multisession)); + get_multi_session_info(&ms_info); + memcpy_tofs((struct cdrom_multisession *) arg, &ms_info, + sizeof(struct cdrom_multisession)); + return 0; + } + case CDROMRESET: /* If needed, it's probably too late anyway */ + stop_read(); + reset_cm260(); + outw(dc_normal | dc_break | READ_AHEAD, r_data_control); + udelay(1000); /* 750 musec minimum */ + outw(dc_normal | READ_AHEAD, r_data_control); + cd->sector_last = -1; /* flag no data buffered */ + cd->adapter_last = -1; + return 0; + } + + get_drive_status(); + if (cd->dsb & (dsb_drive_not_ready | dsb_tray_not_closed) ) + return -EAGAIN; + + switch (cmd) { + case CDROMREADTOCHDR: { + struct cdrom_tochdr header; + int st; + + st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(header)); + if (st) return (st); + if (read_toc_header(&header)) { + memcpy_tofs((struct cdrom_tochdr *) arg, &header, sizeof(header)); + return 0; + } + else return -ENODATA; + } + case CDROMREADTOCENTRY: { + struct cdrom_tocentry entry; + int st; + + st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry)); + if (st) return (st); + memcpy_fromfs(&entry, (struct cdrom_tocentry *) arg, sizeof entry); + get_toc_entry(&entry); + memcpy_tofs((struct cdrom_tocentry *) arg, &entry, sizeof entry); + return 0; + } + case CDROMPLAYMSF: { + struct cdrom_msf msf; + int st; + + st=verify_area(VERIFY_READ, (void *) arg, sizeof(msf)); + if (st) return (st); + memcpy_fromfs(&msf, (struct cdrom_mdf *) arg, sizeof msf); + play_from_to_msf(&msf); + return 0; + } + case CDROMPLAYTRKIND: { + struct cdrom_ti track_index; + int st; + + st=verify_area(VERIFY_READ, (void *) arg, sizeof(track_index)); + if (st) return (st); + memcpy_fromfs(&track_index, (struct cdrom_ti *) arg, sizeof(track_index)); + play_from_to_track(track_index.cdti_trk0, track_index.cdti_trk1); + return 0; + } + case CDROMSTOP: + PAUSED=0; + if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1); + return 0; + case CDROMPAUSE: + if (cd->dsb & dsb_play_in_progress) { + type_0_command(c_stop, 1); + type_1_command(c_audio_status, 5, cd->audio_status); + PAUSED=1; /* say we're paused */ + } + return 0; + case CDROMRESUME: + if (PAUSED) play_from_to_track(0,0); + PAUSED=0; + return 0; + case CDROMEJECT: + PAUSED=0; + if (cd->openfiles == 1) { /* Must do an open before an eject! */ + type_0_command(c_open_tray,1); + memset(cd->toc, 0, sizeof(cd->toc)); + memset(cd->disc_status, 0, sizeof(cd->disc_status)); + return 0; + } + else return -EBUSY; + case CDROMSTART: + case CDROMVOLCTRL: + return 0; + case CDROMSUBCHNL: { + struct cdrom_subchnl q; + int st; + + st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(q)); + if (st) return (st); + memcpy_fromfs(&q, (struct cdrom_subchnl *) arg, sizeof q); + if (get_current_q(&q)) { + memcpy_tofs((struct cdrom_subchnl *) arg, &q, sizeof q); + return 0; + } + else return -cmd; + } + case CDROM_GET_UPC: { + uch upc[10]; + int st; + + st=verify_area(VERIFY_WRITE, (void *) arg, 8); + if (st) return (st); + if (type_1_command(c_read_upc, 10, upc)) return -EIO; + memcpy_tofs((uch *) arg, &upc[1], 8); + return 0; + } + default: + debug(("Unknown ioctl call 0x%x\n", cmd)); + return -EINVAL; + } +} + +/* from lmscd.c */ +static struct file_operations cm206_fops = { + NULL, /* lseek */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir */ + NULL, /* select */ + cm206_ioctl, /* ioctl */ + NULL, /* mmap */ + cm206_open, /* open */ + cm206_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* media_change */ + NULL /* revalidate */ +}; + +/* This routine gets called during init if thing go wrong, can be used + * in cleanup_module as well. */ +void cleanup(int level) +{ + switch (level) { + case 4: + if (unregister_blkdev(MAJOR_NR, "cm206")) { + printk("Can't unregister cm206\n"); + return; + } + case 3: + free_irq(cm206_irq); + case 2: + case 1: + kfree(cd); + release_region(cm206_base, 16); + default: + } +} + +/* This function probes for the adapter card. It returns the base + address if it has found the adapter card. One can specify a base + port to probe specifically, or 0 which means span all possible + bases. + + Linus says it is too dangerous to use writes for probing, so we + stick with pure reads for a while. Hope that 8 possible ranges, + check_region, 15 bits of one port and 6 of another make things + likely enough to accept the region on the first hit... + */ +int probe_base_port(int base) +{ + int b=0x300, e=0x370; /* this is the range of start addresses */ + volatile int fool; +#if 0 + const pattern1=0x65, pattern2=0x1a; +#endif + + if (base) b=e=base; + for (base=b; base<=e; base += 0x10) { + if (check_region(base, 0x10)) continue; + fool = inw(base+2); /* empty possibly uart_receive_buffer */ + if((inw(base+6) & 0xffef) != 0x0001 || /* line_status */ + (inw(base) & 0xad00) != 0) /* data status */ + continue; +#if 0 /* writes... dangerous... */ + outw(dc_normal | pattern1, base+8); + if ((inw(base) & 0x7f) != pattern1) continue; + outw(dc_normal | pattern2, base+8); + if ((inw(base) & 0x7f) != pattern2) continue; + outw(dc_normal | READ_AHEAD, base+8); +#endif + return(base); + } + return 0; +} + +#if !defined(MODULE) || defined(AUTO_PROBE_MODULE) +/* Probe for irq# nr. If nr==0, probe for all possible irq's. */ +int probe_irq(int nr) { + int irqs, irq; + outw(dc_normal | READ_AHEAD, r_data_control); /* disable irq-generation */ + sti(); + irqs = probe_irq_on(); + reset_cm260(); /* causes interrupt */ + udelay(10); /* wait for it */ + irq = probe_irq_off(irqs); + outw(dc_normal | READ_AHEAD, r_data_control); /* services interrupt */ + if (nr && irq!=nr && irq>0) return 0; /* wrong interrupt happened */ + else return irq; +} +#endif + +#ifdef MODULE + +static int cm206[2] = {0,0}; /* for compatible `insmod' parameter passing */ +void parse_options(void) +{ + int i; + for (i=0; i<2; i++) { + if (0x300 <= cm206[i] && i<= 0x370 && cm206[i] % 0x10 == 0) { + cm206_base = cm206[i]; + auto_probe=0; + } + else if (3 <= cm206[i] && cm206[i] <= 15) { + cm206_irq = cm206[i]; + auto_probe=0; + } + } +} + +#define cm206_init init_module + +#endif MODULE + + +int cm206_init(void) +{ + uch e=0; + long int size=sizeof(struct cm206_struct); + + printk("cm206: v" VERSION); +#if defined(MODULE) + parse_options(); +#if !defined(AUTO_PROBE_MODULE) + auto_probe=0; +#endif +#endif + cm206_base = probe_base_port(auto_probe ? 0 : cm206_base); + if (!cm206_base) { + printk(" can't find adapter!\n"); + return -EIO; + } + printk(" adapter at 0x%x", cm206_base); + request_region(cm206_base, 16, "cm206"); + cd = (struct cm206_struct *) kmalloc(size, GFP_KERNEL); + if (!cd) return -EIO; + /* Now we have found the adaptor card, try to reset it. As we have + * found out earlier, this process generates an interrupt as well, + * so we might just exploit that fact for irq probing! */ +#if !defined(MODULE) || defined(AUTO_PROBE_MODULE) + cm206_irq = probe_irq(auto_probe ? 0 : cm206_irq); + if (cm206_irq<=0) { + printk("can't find IRQ!\n"); + cleanup(1); + return -EIO; + } + else printk(" IRQ %d found\n", cm206_irq); +#else + reset_cm260(); + printk(" using IRQ %d\n", cm206_irq); +#endif + if (send_receive_polled(c_drive_configuration) != c_drive_configuration) + { + printk(" drive not there\n"); + cleanup(1); + return -EIO; + } + e = send_receive_polled(c_gimme); + printk("Firmware revision %d", e & dcf_revision_code); + if (e & dcf_transfer_rate) printk(" double"); + else printk(" single"); + printk(" speed drive"); + if (e & dcf_motorized_tray) printk(", motorized tray"); + if (request_irq(cm206_irq, cm206_interrupt, 0, "cm206")) { + printk("\nUnable to reserve IRQ---aborted\n"); + cleanup(2); + return -EIO; + } + printk(".\n"); + if (register_blkdev(MAJOR_NR, "cm206", &cm206_fops) != 0) { + printk("Cannot register for major %d!\n", MAJOR_NR); + cleanup(3); + return -EIO; + } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 16; /* reads ahead what? */ + bh_base[CM206_BH].routine = cm206_bh; + enable_bh(CM206_BH); + + memset(cd, 0, sizeof(*cd)); /* give'm some reasonable value */ + cd->sector_last = -1; /* flag no data buffered */ + cd->adapter_last = -1; + cd->timer.function = cm206_timeout; + cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97; + printk("%d kB adapter memory available, " + " %ld bytes kernel memory used.\n", cd->max_sectors*2, size); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + cleanup(4); + printk("cm206 removed\n"); +} + +#else MODULE + +/* This setup function accepts either `auto' or numbers in the range + * 3--11 (for irq) or 0x300--0x370 (for base port) or both. */ +void cm206_setup(char *s, int *p) +{ + int i; + if (!strcmp(s, "auto")) auto_probe=1; + for(i=1; i<=p[0]; i++) { + if (0x300 <= p[i] && i<= 0x370 && p[i] % 0x10 == 0) { + cm206_base = p[i]; + auto_probe = 0; + } + else if (3 <= p[i] && p[i] <= 15) { + cm206_irq = p[i]; + auto_probe = 0; + } + } +} +#endif MODULE diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/gscd.c linux/drivers/cdrom/gscd.c --- v1.3.35/linux/drivers/cdrom/gscd.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/gscd.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,1126 @@ +#define GSCD_VERSION "0.4a Oliver Raupach " + +/* + linux/drivers/block/gscd.c - GoldStar R420 CDROM driver + + Copyright (C) 1995 Oliver Raupach + based upon pre-works by Eberhard Moenkeberg + + + 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 + +#ifdef MODULE +#include +#include +#include +#ifndef CONFIG_MODVERSIONS +char kernel_version[] = UTS_RELEASE; +#endif +#endif MODULE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAJOR_NR GOLDSTAR_CDROM_MAJOR +#include +#define gscd_port gscd /* for compatible parameter passing with "insmod" */ +#include + + +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 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 0 ); + + cmd_end (); + return; +} + +int find_drives (void) +{ +int *pdrv; +int drvnum; +int subdrv; +int i; + + speed = 0; + pdrv = (int *)&drv_states; + curr_drv_state = 0xFE; + subdrv = 0; + drvnum = 0; + + for ( i=0 ; i<8 ; i++ ) + { + subdrv++; + cmd_status (); + disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01; + if ( disk_state != (ST_x08 | ST_x04 | ST_INVALID) ) + { + /* LOC_240 */ + *pdrv = curr_drv_state; + init_cd_drive (drvnum); + pdrv++; + drvnum++; + } + else + { + if ( subdrv < 2 ) + { + continue; + } + else + { + subdrv = 0; + } + } + +/* curr_drv_state<<1; <-- das geht irgendwie nicht */ +/* muss heissen: curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */ + curr_drv_state *= 2; + curr_drv_state |= 1; +#ifdef GSCD_DEBUG + printk ("DriveState: %d\n", curr_drv_state ); +#endif + } + + ndrives = drvnum; + return drvnum; +} + +void init_cd_drive ( int num ) +{ +char resp [50]; +int i; + + printk ("GSCD: init unit %d\n", num ); + cc_Ident ((char *)&resp); + + printk ("GSCD: identification: "); + for ( i=0 ; i<0x1E; i++ ) + { + printk ( "%c", resp[i] ); + } + printk ("\n"); + + cc_SetSpeed (); + +} + +#ifdef FUTURE_WORK +/* return_done */ +static void update_state ( void ) +{ +unsigned int AX; + + + if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 ) + { + if ( disk_state == (ST_x08 | ST_x04 | ST_INVALID)) + { + AX = ST_INVALID; + } + + if ( (disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0 ) + { + invalidate (); + f_drv_ok = 0; + } + + AX |= 0x8000; + } + + if ( disk_state & ST_PLAYING ) + { + AX |= 0x200; + } + + AX |= 0x100; + /* pkt_esbx = AX; */ + + disk_state = 0; + +} +#endif + +/* Init for the Module-Version */ +int init_module (void) +{ +long err; + + + /* call the GoldStar-init */ + err = my_gscd_init ( ); + + if ( err < 0 ) + { + return err; + } + else + { + printk ( "Happy GoldStar !\n" ); + return 0; + } +} + +#ifdef MODULE +void cleanup_module (void) +{ + + if (MOD_IN_USE) + { + printk("GoldStar-module in use - can't remove it.\n" ); + return; + } + + if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL)) + { + printk("What's that: can't unregister GoldStar-module\n" ); + return; + } + + release_region (gscd_port,4); + printk( "GoldStar-module released.\n" ); +} +#endif + + +/* Test for presence of drive and initialize it. Called only at boot time. */ +int gscd_init (void) +{ + return my_gscd_init (); +} + + +/* This is the common initalisation for the GoldStar drive. */ +/* It is called at boot time AND for module init. */ +int my_gscd_init (void) +{ +int i; +int result; + + printk ("GSCD: version %s\n", GSCD_VERSION); + printk ("GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", gscd_port); + + if (check_region(gscd_port, 4)) + { + printk("GSCD: Init failed, I/O port (%X) already in use.\n", gscd_port); + return -EIO; + } + + + /* check for card */ + result = wait_drv_ready (); + if ( result == 0x09 ) + { + printk ("GSCD: DMA kann ich noch nicht!\n" ); + return -EIO; + } + + if ( result == 0x0b ) + { + drv_mode = result; + i = find_drives (); + if ( i == 0 ) + { + printk ( "GSCD: GoldStar CD-ROM Drive is not found.\n" ); + return -EIO; + } + } + + if ( (result != 0x0b) && (result != 0x09) ) + { + printk ("GSCD: GoldStar Interface Adapter does not exist or H/W error\n" ); + return -EIO; + } + + /* reset all drives */ + i = 0; + while ( drv_states[i] != 0 ) + { + curr_drv_state = drv_states[i]; + printk ( "GSCD: Reset unit %d ... ",i ); + cc_Reset (); + printk ( "done\n" ); + i++; + } + + if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0) + { + printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n", + MAJOR_NR); + return -EIO; + } + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 4; + + disk_state = 0; + gscdPresent = 1; + + request_region(gscd_port, 4, "gscd"); + + printk ( "GSCD: GoldStar CD-ROM Drive found.\n" ); + return 0; +} + +static void gscd_hsg2msf (long hsg, struct msf *msf) +{ + hsg += CD_BLOCK_OFFSET; + msf -> min = hsg / (CD_FRAMES*CD_SECS); + hsg %= CD_FRAMES*CD_SECS; + msf -> sec = hsg / CD_FRAMES; + msf -> frame = hsg % CD_FRAMES; + + gscd_bin2bcd(&msf -> min); /* convert to BCD */ + gscd_bin2bcd(&msf -> sec); + gscd_bin2bcd(&msf -> frame); +} + + +static void gscd_bin2bcd (unsigned char *p) +{ +int u, t; + + u = *p % 10; + t = *p / 10; + *p = u | (t << 4); +} + + +#ifdef FUTURE_WOTK +static long gscd_msf2hsg (struct msf *mp) +{ + return gscd_bcd2bin(mp -> frame) + + gscd_bcd2bin(mp -> sec) * CD_FRAMES + + gscd_bcd2bin(mp -> min) * CD_FRAMES * CD_SECS + - CD_BLOCK_OFFSET; +} + +static int gscd_bcd2bin (unsigned char bcd) +{ + return (bcd >> 4) * 10 + (bcd & 0xF); +} +#endif + + diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c --- v1.3.35/linux/drivers/cdrom/mcd.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/mcd.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,1632 @@ +/* + linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver + + Copyright (C) 1992 Martin Harriss + + martin@bdsi.com (no longer valid - where are you now, Martin?) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + HISTORY + + 0.1 First attempt - internal use only + 0.2 Cleaned up delays and use of timer - alpha release + 0.3 Audio support added + 0.3.1 Changes for mitsumi CRMC LU005S march version + (stud11@cc4.kuleuven.ac.be) + 0.3.2 bug fixes to the ioctls and merged with ALPHA0.99-pl12 + (Jon Tombs ) + 0.3.3 Added more #defines and mcd_setup() + (Jon Tombs ) + + 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 + 07 July 1995 Modifications by Andrew J. Kroll + +*/ + +#include + +#ifdef MODULE +# include +# include +# 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* #define REALLY_SLOW_IO */ +#include +#include +#include + +#define MAJOR_NR MITSUMI_CDROM_MAJOR +#include +#define mcd_port mcd /* for compatible parameter passing with "insmod" */ +#include + +#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 /* */ +#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("", st); +#endif + if (!(st & MFL_STATUS)) + { + st = inb(MCDPORT(0)) & 0xFF; +#ifdef TEST1 + printk("", 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 diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c --- v1.3.35/linux/drivers/cdrom/mcdx.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/mcdx.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,1727 @@ +/* + * The Mitsumi CDROM interface + * Copyright (C) 1995 Heiko Schlittermann + * VERSION: 1.0a + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Thanks to + * The Linux Community at all and ... + * Martin Harriss (he wrote the first Mitsumi Driver) + * Eberhard Moenkeberg (he gave me much support and the initial kick) + * Bernd Huebner, Ruediger Helsch (Unifix-Software GmbH, they + * improved the original driver) + * Jon Tombs, Bjorn Ekwall (module support) + * Daniel v. Mosnenck (he sent me the Technical and Programming Reference) + * Gerd Knorr (he lent me his PhotoCD) + * Nils Faerber and Roger E. Wolff (extensivly tested the LU portion) + * ... somebody forgotten? + * + */ + + +#if RCS +static const char *mcdx_c_version + = "mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp"; +#endif + +#include +#ifdef MODULE +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#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 +#define mcdx_drive_map mcdx /* for compatible parameter passing with "insmod" */ +#include + +/* CONSTANTS *******************************************************/ + +const int REQUEST_SIZE = 200; +const int DIRECT_SIZE = 200; + +enum drivemodes { TOC, DATA, RAW, COOKED }; +enum datamodes { MODE0, MODE1, MODE2 }; +enum resetmodes { SOFT, HARD }; + +const int SINGLE = 0x01; +const int DOUBLE = 0x02; +const int DOOR = 0x04; +const int MULTI = 0x08; +const int READY = 0x70; + +const unsigned char READSSPEED = 0xc0; +const unsigned char READDSPEED = 0xc1; + + +/* DECLARATIONS ****************************************************/ +struct s_msf { + unsigned char minute; + unsigned char second; + unsigned char frame; +}; + +struct s_subqcode { + unsigned char control; + unsigned char tno; + unsigned char index; + struct s_msf tt; + struct s_msf dt; +}; + +struct s_diskinfo { + unsigned int n_first; + unsigned int n_last; + struct s_msf msf_leadout; + struct s_msf msf_first; +}; + +struct s_multi { + unsigned char multi; + struct s_msf msf_last; +}; + +struct s_version { + unsigned char code; + unsigned char ver; +}; + +/* Per drive/controller stuff **************************************/ + +struct s_drive_stuff { + /* waitquenes */ + struct wait_queue *busyq; + struct wait_queue *lockq; + struct wait_queue *sleepq; + + /* flags */ + volatile int introk; /* status of last irq operation */ + volatile int busy; /* drive performs an operation */ + volatile int lock; /* exclusive usage */ + + /* cd infos */ + struct s_diskinfo di; + struct s_multi multi; + struct s_subqcode* toc; /* first enty of the toc array */ + struct s_subqcode start; + struct s_subqcode stop; + int xa; /* 1 if xa disk */ + int audio; /* 1 if audio disk */ + int audiostatus; + + /* `buffer' control */ + volatile int valid; + volatile int pending; + volatile int off_direct; + volatile int off_requested; + + /* adds and odds */ + void* wreg_data; /* w data */ + void* wreg_reset; /* w hardware reset */ + void* wreg_hcon; /* w hardware conf */ + void* wreg_chn; /* w channel */ + void* rreg_data; /* r data */ + void* rreg_status; /* r status */ + + int irq; /* irq used by this drive */ + int minor; /* minor number of this drive */ + int present; /* drive present and its capabilities */ + char readcmd; /* read cmd depends on single/double speed */ + char playcmd; /* play should always be single speed */ + unsigned long changed; /* last jiff the media was changed */ + unsigned long xxx; /* last jiff it was asked for media change */ + int users; /* keeps track of open/close */ + int lastsector; /* last block accessible */ + int errno; /* last operation's error */ + +}; + + +/* Prototypes ******************************************************/ + +/* The following prototypes are already declared elsewhere. They are + repeated here to show what's going on. And to sense, if they're + changed elsewhere. */ + +/* declared in blk.h */ +int mcdx_init(void); +void do_mcdx_request(void); + +int check_mcdx_media_change(kdev_t); + +/* already declared in init/main */ +void mcdx_setup(char *, int *); + +/* Indirect exported functions. These functions are exported by their + addresses, such as mcdx_open and mcdx_close in the + structure fops. */ + +/* ??? exported by the mcdx_sigaction struct */ +static void mcdx_intr(int, struct pt_regs*); + +/* exported by file_ops */ +static int mcdx_open(struct inode*, struct file*); +static void mcdx_close(struct inode*, struct file*); +static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long); + +/* misc internal support functions */ +static void log2msf(unsigned int, struct s_msf*); +static unsigned int msf2log(const struct s_msf*); +static unsigned int uint2bcd(unsigned int); +static unsigned int bcd2uint(unsigned char); +#if MCDX_DEBUG +static void TRACE((int level, const char* fmt, ...)); +#endif +static void warn(const char* fmt, ...); +static char *port(int*); +static int irq(int*); +static void mcdx_delay(struct s_drive_stuff*, long jifs); +static int mcdx_transfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors); + +static int mcdx_config(struct s_drive_stuff*, int); +static int mcdx_closedoor(struct s_drive_stuff*, int); +static int mcdx_requestversion(struct s_drive_stuff*, struct s_version*, int); +static int mcdx_lockdoor(struct s_drive_stuff*, int, int); +static int mcdx_stop(struct s_drive_stuff*, int); +static int mcdx_hold(struct s_drive_stuff*, int); +static int mcdx_reset(struct s_drive_stuff*, enum resetmodes, int); +static int mcdx_eject(struct s_drive_stuff*, int); +static int mcdx_setdrivemode(struct s_drive_stuff*, enum drivemodes, int); +static int mcdx_setdatamode(struct s_drive_stuff*, enum datamodes, int); +static int mcdx_requestsubqcode(struct s_drive_stuff*, struct s_subqcode*, int); +static int mcdx_requestmultidiskinfo(struct s_drive_stuff*, struct s_multi*, int); +static int mcdx_requesttocdata(struct s_drive_stuff*, struct s_diskinfo*, int); +static int mcdx_getstatus(struct s_drive_stuff*, int); +static int mcdx_getval(struct s_drive_stuff*, int to, int delay, char*); + +static int mcdx_talk(struct s_drive_stuff*, + const unsigned char* cmd, size_t, void *buffer, + size_t size, unsigned int timeout, int); +static int mcdx_readtoc(struct s_drive_stuff*); +static int mcdx_playtrk(struct s_drive_stuff*, const struct cdrom_ti*); +static int mcdx_playmsf(struct s_drive_stuff*, const struct cdrom_msf*); + +/* static variables ************************************************/ + +static int dummy0; +static int mcdx_drive_map[][2] = MCDX_DRIVEMAP; +static struct s_drive_stuff* mcdx_stuffp[MCDX_NDRIVES]; +static struct s_drive_stuff* mcdx_irq_map[16] = + {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + +static struct file_operations mcdx_fops = { + NULL, /* lseek - use kernel default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* no readdir */ + NULL, /* no select */ + mcdx_ioctl, /* ioctl() */ + NULL, /* no mmap */ + mcdx_open, /* open() */ + mcdx_close, /* close() */ + NULL, /* fsync */ + NULL, /* fasync */ + check_mcdx_media_change, /* media_change */ + NULL /* revalidate */ +}; + +/* KERNEL INTERFACE FUNCTIONS **************************************/ + +static int +mcdx_ioctl( + struct inode* ip, struct file* fp, + unsigned int cmd, unsigned long arg) +{ + struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; + + if (!stuffp->present) return -ENXIO; + if (!ip) return -EINVAL; + + switch (cmd) { + case CDROMSTART: { + TRACE((IOCTL, "ioctl() START\n")); + return 0; + } + + case CDROMSTOP: { + TRACE((IOCTL, "ioctl() STOP\n")); + stuffp->audiostatus = CDROM_AUDIO_INVALID; + if (-1 == mcdx_stop(stuffp, 1)) + return -EIO; + return 0; + } + + case CDROMPLAYTRKIND: { + int ans; + struct cdrom_ti ti; + + TRACE((IOCTL, "ioctl() PLAYTRKIND\n")); + if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti)))) + return ans; + memcpy_fromfs(&ti, (void*) arg, sizeof(ti)); + if ((ti.cdti_trk0 < stuffp->di.n_first) + || (ti.cdti_trk0 > stuffp->di.n_last) + || (ti.cdti_trk1 < stuffp->di.n_first)) + return -EINVAL; + if (ti.cdti_trk1 > stuffp->di.n_last) ti.cdti_trk1 = stuffp->di.n_last; + TRACE((IOCTL, "ioctl() track %d to %d\n", ti.cdti_trk0, ti.cdti_trk1)); + + return mcdx_playtrk(stuffp, &ti); + } + + case CDROMPLAYMSF: { + int ans; + struct cdrom_msf msf; + + TRACE((IOCTL, "ioctl() PLAYMSF\n")); + + if ((stuffp->audiostatus == CDROM_AUDIO_PLAY) + && (-1 == mcdx_hold(stuffp, 1))) return -EIO; + + if ((ans = verify_area( + VERIFY_READ, (void*) arg, sizeof(struct cdrom_msf)))) + return ans; + + memcpy_fromfs(&msf, (void*) arg, sizeof msf); + + msf.cdmsf_min0 = uint2bcd(msf.cdmsf_min0); + msf.cdmsf_sec0 = uint2bcd(msf.cdmsf_sec0); + msf.cdmsf_frame0 = uint2bcd(msf.cdmsf_frame0); + + msf.cdmsf_min1 = uint2bcd(msf.cdmsf_min1); + msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1); + msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1); + + return mcdx_playmsf(stuffp, &msf); + } + + case CDROMRESUME: { + TRACE((IOCTL, "ioctl() RESUME\n")); + return mcdx_playtrk(stuffp, NULL); + } + + case CDROMREADTOCENTRY: { + struct cdrom_tocentry entry; + struct s_subqcode *tp = NULL; + int ans; + + TRACE((IOCTL, "ioctl() READTOCENTRY\n")); + + if (-1 == mcdx_readtoc(stuffp)) return -1; + + if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(entry)))) return ans; + memcpy_fromfs(&entry, (void *) arg, sizeof(entry)); + + if (entry.cdte_track == CDROM_LEADOUT) + tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1]; + else if (entry.cdte_track > stuffp->di.n_last + || entry.cdte_track < stuffp->di.n_first) return -EINVAL; + else tp = &stuffp->toc[entry.cdte_track - stuffp->di.n_first]; + + if (NULL == tp) WARN(("FATAL.\n")); + + entry.cdte_adr = tp->control; + entry.cdte_ctrl = tp->control >> 4; + + if (entry.cdte_format == CDROM_MSF) { + entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute); + entry.cdte_addr.msf.second = bcd2uint(tp->dt.second); + entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame); + } else if (entry.cdte_format == CDROM_LBA) + entry.cdte_addr.lba = msf2log(&tp->dt); + else return -EINVAL; + + if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(entry)))) return ans; + memcpy_tofs((void*) arg, &entry, sizeof(entry)); + + return 0; + } + + case CDROMSUBCHNL: { + int ans; + struct cdrom_subchnl sub; + struct s_subqcode q; + + TRACE((IOCTL, "ioctl() SUBCHNL\n")); + + if ((ans = verify_area(VERIFY_READ, + (void*) arg, sizeof(sub)))) return ans; + + memcpy_fromfs(&sub, (void*) arg, sizeof(sub)); + + if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO; + + TRACE((IOCTL, "audiostatus: %x\n", stuffp->audiostatus)); + sub.cdsc_audiostatus = stuffp->audiostatus; + sub.cdsc_adr = q.control; + sub.cdsc_ctrl = q.control >> 4; + sub.cdsc_trk = bcd2uint(q.tno); + sub.cdsc_ind = bcd2uint(q.index); + + TRACE((IOCTL, "trk %d, ind %d\n", + sub.cdsc_trk, sub.cdsc_ind)); + + if (sub.cdsc_format == CDROM_LBA) { + sub.cdsc_absaddr.lba = msf2log(&q.dt); + sub.cdsc_reladdr.lba = msf2log(&q.tt); + TRACE((IOCTL, "lba: abs %d, rel %d\n", + sub.cdsc_absaddr.lba, + sub.cdsc_reladdr.lba)); + } else if (sub.cdsc_format == CDROM_MSF) { + sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute); + sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second); + sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame); + sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute); + sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second); + sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame); + TRACE((IOCTL, + "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n", + sub.cdsc_absaddr.msf.minute, + sub.cdsc_absaddr.msf.second, + sub.cdsc_absaddr.msf.frame, + sub.cdsc_reladdr.msf.minute, + sub.cdsc_reladdr.msf.second, + sub.cdsc_reladdr.msf.frame)); + } else return -EINVAL; + + if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof(sub)))) + return ans; + memcpy_tofs((void*) arg, &sub, sizeof(sub)); + + return 0; + } + + case CDROMREADTOCHDR: { + struct cdrom_tochdr toc; + int ans; + + TRACE((IOCTL, "ioctl() READTOCHDR\n")); + if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof toc))) + return ans; + toc.cdth_trk0 = stuffp->di.n_first; + toc.cdth_trk1 = stuffp->di.n_last; + memcpy_tofs((void*) arg, &toc, sizeof toc); + TRACE((IOCTL, "ioctl() track0 = %d, track1 = %d\n", + stuffp->di.n_first, stuffp->di.n_last)); + return 0; + } + + case CDROMPAUSE: { + TRACE((IOCTL, "ioctl() PAUSE\n")); + if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL; + if (-1 == mcdx_stop(stuffp, 1)) return -EIO; + if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1)) + return -EIO; + + stuffp->audiostatus = CDROM_AUDIO_PAUSED; + return 0; + } + + case CDROMMULTISESSION: { + int ans; + struct cdrom_multisession ms; + TRACE((IOCTL, "ioctl() MULTISESSION\n")); + if (0 != (ans = verify_area(VERIFY_READ, (void*) arg, + sizeof(struct cdrom_multisession)))) + return ans; + + memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession)); + if (ms.addr_format == CDROM_MSF) { + ms.addr.msf.minute = bcd2uint(stuffp->multi.msf_last.minute); + ms.addr.msf.second = bcd2uint(stuffp->multi.msf_last.second); + ms.addr.msf.frame = bcd2uint(stuffp->multi.msf_last.frame); + } else if (ms.addr_format == CDROM_LBA) + ms.addr.lba = msf2log(&stuffp->multi.msf_last); + else + return -EINVAL; + ms.xa_flag = stuffp->xa; + + if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg, + sizeof(struct cdrom_multisession)))) + return ans; + + memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession)); + if (ms.addr_format == CDROM_MSF) + TRACE((IOCTL, + "ioctl() (%d, %02x:%02x.%02x [%02x:%02x.%02x])\n", + ms.xa_flag, + ms.addr.msf.minute, + ms.addr.msf.second, + ms.addr.msf.frame, + stuffp->multi.msf_last.minute, + stuffp->multi.msf_last.second, + stuffp->multi.msf_last.frame)); + else + { + dummy0=0; + TRACE((IOCTL, + "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n", + ms.xa_flag, + ms.addr.lba, + stuffp->multi.msf_last.minute, + stuffp->multi.msf_last.second, + stuffp->multi.msf_last.frame)); + } + return 0; + } + + case CDROMEJECT: { + TRACE((IOCTL, "ioctl() EJECT\n")); + if (stuffp->users > 1) return -EBUSY; + if (-1 == mcdx_eject(stuffp, 1)) return -EIO; + return 0; + } + + case CDROMVOLCTRL: { + TRACE((IOCTL, "ioctl() volctrl\n")); + return 0; + } + + default: + WARN(("ioctl(): unknown request 0x%04x\n", cmd)); + return -EINVAL; + } +} + +void do_mcdx_request() +{ + int dev; + struct s_drive_stuff *stuffp; + + again: + + TRACE((REQUEST, "do_request()\n")); + + if ((CURRENT == NULL) || (CURRENT->rq_status == RQ_INACTIVE)) { + TRACE((REQUEST, "do_request() done\n")); + return; + } + + stuffp = mcdx_stuffp[MINOR(CURRENT->rq_dev)]; + TRACE((REQUEST, "do_request() stuffp = %p\n", stuffp)); + + INIT_REQUEST; + dev = MINOR(CURRENT->rq_dev); + + if ((dev < 0) || (dev >= MCDX_NDRIVES) || (!stuffp->present)) { + WARN(("do_request(): bad device: %s\n", + kdevname(CURRENT->rq_dev))); + end_request(0); + goto again; + } + + if (stuffp->audio) { + WARN(("do_request() attempt to read from audio cd\n")); + end_request(0); + goto again; + } + + switch (CURRENT->cmd) { + case WRITE: + WARN(("do_request(): attempt to write to cd!!\n")); + end_request(0); + break; + + case READ: + stuffp->errno = 0; + while (CURRENT->nr_sectors) { + int i; + + if (-1 == (i = mcdx_transfer( + stuffp, + CURRENT->buffer, + CURRENT->sector, + CURRENT->nr_sectors))) { + WARN(("do_request() read error\n")); + if (stuffp->errno == MCDX_EOM) { + CURRENT->sector += CURRENT->nr_sectors; + CURRENT->nr_sectors = 0; + } + end_request(0); + goto again; + } + CURRENT->sector += i; + CURRENT->nr_sectors -= i; + CURRENT->buffer += (i * 512); + + } + + end_request(1); + break; + + default: + panic(MCDX "do_request: unknown command.\n"); + break; + } + + goto again; +} + +static int +mcdx_open(struct inode *ip, struct file *fp) +{ + struct s_drive_stuff *stuffp; + + TRACE((OPENCLOSE, "open()\n")); + stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; + if (!stuffp->present) return -ENXIO; + + if (-1 == mcdx_getstatus(stuffp, 1)) return -EIO; + + /* close the door, if necessary (get the door information + from the hardware status register) */ + if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR) + mcdx_closedoor(stuffp, 1); + + /* if the media changed we will have to little more */ + if (stuffp->xxx < stuffp->changed) { + + TRACE((OPENCLOSE, "open() media changed\n")); + /* but wait - the time of media change will be set at the + very last of this block - it seems, some of the following + talk() will detect a media change ... (I think, config() + is the reason. */ + + /* + TRACE((OPENCLOSE, "open() hardware reset\n")); + if (-1 == mcdx_reset(stuffp, HARD, 1)) return -EIO; + */ + + stuffp->audiostatus = CDROM_AUDIO_INVALID; + + /* get the multisession information */ + { + TRACE((OPENCLOSE, "open() Request multisession info\n")); + if (-1 == mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6)) + return -EIO; + + if (stuffp->multi.multi > 2) + WARN(("open() unknown multisession value (%d)\n", stuffp->multi.multi)); + + /* multisession ? */ + if (!stuffp->multi.multi) + stuffp->multi.msf_last.second = 2; + + TRACE((OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n", + stuffp->multi.multi, + stuffp->multi.msf_last.minute, + stuffp->multi.msf_last.second, + stuffp->multi.msf_last.frame)); + } /* got multisession information */ + + /* request the disks table of contents (aka diskinfo) */ + if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) return -EIO; + + stuffp->lastsector = (CD_FRAMESIZE / 512) + * msf2log(&stuffp->di.msf_leadout) - 1; + + TRACE((OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n", + stuffp->di.n_first, + stuffp->di.msf_first.minute, + stuffp->di.msf_first.second, + stuffp->di.msf_first.frame, + msf2log(&stuffp->di.msf_first))); + TRACE((OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n", + stuffp->di.n_last, + stuffp->di.msf_leadout.minute, + stuffp->di.msf_leadout.second, + stuffp->di.msf_leadout.frame, + msf2log(&stuffp->di.msf_leadout))); + + if (stuffp->toc) { + TRACE((MALLOC, "open() free toc @ %p\n", stuffp->toc)); + kfree(stuffp->toc); + } + stuffp->toc = NULL; + + TRACE((OPENCLOSE, "open() init irq generation\n")); + if (-1 == mcdx_config(stuffp, 1)) return -EIO; + + /* try to get the first sector ... */ + { + char buf[512]; + int ans; + int tries; + + stuffp->xa = 0; + stuffp->audio = 0; + + for (tries = 6; tries; tries--) { + TRACE((OPENCLOSE, "open() try as %s\n", + stuffp->xa ? "XA" : "normal")); + + /* set data mode */ + if (-1 == (ans = mcdx_setdatamode(stuffp, + stuffp->xa ? MODE2 : MODE1, 1))) + return -EIO; + + if ((stuffp->audio = e_audio(ans))) break; + + while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) + ; + + if (ans == 1) break; + stuffp->xa = !stuffp->xa; + } + if (!tries) return -EIO; + } + + /* xa disks will be read in raw mode, others not */ + if (-1 == mcdx_setdrivemode(stuffp, + stuffp->xa ? RAW : COOKED, 1)) + return -EIO; + + if (stuffp->audio) { + INFO(("open() audio disk found\n")); + } else { + INFO(("open() %s%s disk found\n", + stuffp->xa ? "XA / " : "", + stuffp->multi.multi ? "Multi Session" : "Single Session")); + } + + stuffp->xxx = jiffies; + } + + /* lock the door if not already done */ + if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1))) + return -EIO; + + stuffp->users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void +mcdx_close(struct inode *ip, struct file *fp) +{ + struct s_drive_stuff *stuffp; + + TRACE((OPENCLOSE, "close()\n")); + + stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; + + if (0 == --stuffp->users) { + sync_dev(ip->i_rdev); /* needed for r/o device? */ + + /* invalidate_inodes(ip->i_rdev); */ + invalidate_buffers(ip->i_rdev); + + if (-1 == mcdx_lockdoor(stuffp, 0, 1)) + printk(MCDX ": Cannot unlock the door\n"); + } + MOD_DEC_USE_COUNT; + + return; +} + +int check_mcdx_media_change(kdev_t full_dev) +/* Return: 1 if media changed since last call to + this function + 0 else + Setting flag to 0 resets the changed state. */ + +{ + INFO(("check_mcdx_media_change called for device %s\n", + kdevname(full_dev))); + return 0; +} + +void mcdx_setup(char *str, int *pi) +{ +#if MCDX_DEBUG + printk(MCDX ":: setup(%s, %d) called\n", + str, pi[0]); +#endif +} + +/* DIRTY PART ******************************************************/ + +static void mcdx_delay(struct s_drive_stuff *stuff, long jifs) +/* This routine is used for sleeping while initialisation - it seems that + there are no other means available. May be we could use a simple count + loop w/ jumps to itself, but I wanna make this independend of cpu + speed. [1 jiffie is 1/HZ sec */ +{ + unsigned long tout = jiffies + jifs; + + TRACE((INIT, "mcdx_delay %d\n", jifs)); + if (jifs < 0) return; + +#if 1 + while (jiffies < tout) { + current->timeout = jiffies; + schedule(); + } +#else + if (current->pid == 0) { /* no sleep allowed */ + while (jiffies < tout) { + current->timeout = jiffies; + schedule(); + } + } else { /* sleeping is allowed */ + current->timeout = tout; + current->state = TASK_INTERRUPTIBLE; + while (current->timeout) { + interruptible_sleep_on(&stuff->sleepq); + } + } +#endif +} + +static void +mcdx_intr(int irq, struct pt_regs* regs) +{ + struct s_drive_stuff *stuffp; + unsigned char x; + + stuffp = mcdx_irq_map[irq]; + + if (!stuffp->busy) { + INFO(("intr() unexpected interrupt @ irq %d\n", irq)); + return; + } + + /* if not ok read the next byte as the drives status */ + if (0 == (stuffp->introk = + (~(x = inb((unsigned int) stuffp->rreg_status)) & MCDX_RBIT_DTEN))) + TRACE((IRQ, "intr() irq %d failed, status %02x %02x\n", + irq, x, inb((unsigned int) stuffp->rreg_data))); + else + { + dummy0=0; + TRACE((IRQ, "irq() irq %d ok, status %02x\n", irq, x)); + } + stuffp->busy = 0; + wake_up_interruptible(&stuffp->busyq); +} + + +static int +mcdx_talk ( + struct s_drive_stuff *stuffp, + const unsigned char *cmd, size_t cmdlen, + void *buffer, size_t size, + unsigned int timeout, int tries) +/* Send a command to the drive, wait for the result. + * returns -1 on timeout, drive status otherwise + */ +{ + char c; + int st; + + if (!buffer || size == 0) buffer = &c, size = 1; + + while (stuffp->lock) + interruptible_sleep_on(&stuffp->lockq); + + if (current->signal && ~current->blocked) { + WARN(("talk() got signal %d\n", current->signal)); + return -1; + } + + stuffp->lock = 1; + stuffp->valid = 0; + +#if MCDX_DEBUG & TALK + { + unsigned char i; + TRACE((TALK, "talk() %d / %d tries, res.size %d, command 0x%02x", + tries, timeout, size, (unsigned char) cmd[0])); + for (i = 1; i < cmdlen; i++) printk(" 0x%02x", cmd[i]); + printk("\n"); + } +#endif + + /* give up if all tries are done (bad) or if the status + * st != -1 (good) */ + for (st = -1; st == -1 && tries; tries--) { + + size_t sz = size; + char* bp = buffer; + + outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen); + TRACE((TALK, "talk() command sent\n")); + + /* get the status byte */ + if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) { + WARN(("talk() %02x timed out (status), %d tr%s left\n", + cmd[0], tries - 1, tries == 2 ? "y" : "ies")); + continue; + } + st = *bp++; + sz--; + + TRACE((TALK, "talk() got status 0x%02x\n", st)); + + /* command error? */ + if (e_cmderr(st)) { + WARN(("command error %02x (%d)\n", cmd[0], cmdlen)); + st = -1; + continue; + } + + /* audio status? */ + if (stuffp->audiostatus == CDROM_AUDIO_INVALID) + stuffp->audiostatus = + e_audiobusy(st) ? CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS; + + /* media change? */ + if (e_changed(st)) { + INFO(("talk() media changed\n")); + stuffp->changed = jiffies; + } + + /* now actually get the data */ + while (sz--) { + if (-1 == mcdx_getval(stuffp, timeout, -1, bp++)) { + WARN(("talk() %02x timed out (data), %d tr%s left\n", + cmd[0], tries - 1, tries == 2 ? "y" : "ies")); + st = -1; break; + } + TRACE((TALK, "talk() got 0x%02x\n", *(bp - 1))); + } + } + +#if QUIET == 0 + if (!tries && st == -1) INFO(("talk() giving up\n")); +#endif + + stuffp->lock = 0; + wake_up_interruptible(&stuffp->lockq); + + TRACE((TALK, "talk() done with 0x%02x\n", st)); + return st; +} + +/* MODULE STUFF ***********************************************************/ +#ifdef MODULE + +int init_module(void) +{ + int i; + int drives = 0; + + mcdx_init(); + for (i = 0; i < MCDX_NDRIVES; i++) { + if (mcdx_stuffp[i]) { + TRACE((INIT, "init_module() drive %d stuff @ %p\n", + i, mcdx_stuffp[i])); + drives++; + } + } + + if (!drives) + return -EIO; + + return 0; +} + +void cleanup_module(void) +{ + int i; + + WARN(("cleanup_module called\n")); + + for (i = 0; i < MCDX_NDRIVES; i++) { + struct s_drive_stuff *stuffp; + stuffp = mcdx_stuffp[i]; + if (!stuffp) continue; + release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE); + free_irq(stuffp->irq); + if (stuffp->toc) { + TRACE((MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc)); + kfree(stuffp->toc); + } + TRACE((MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp)); + mcdx_stuffp[i] = NULL; + kfree(stuffp); + } + + if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) + WARN(("cleanup() unregister_blkdev() failed\n")); + else INFO(("cleanup() succeeded\n")); +} + +#endif MODULE + +/* Support functions ************************************************/ + +#if MCDX_DEBUG +void trace(int level, const char* fmt, ...) +{ + char s[255]; + va_list args; + if (level < 1) return; + va_start(args, fmt); + if (sizeof(s) < vsprintf(s, fmt, args)) + printk(MCDX ":: dprintf exeeds limit!!\n"); + else printk(MCDX ":: %s", s); + va_end(args); +} +#endif + +void warn(const char* fmt, ...) +{ + char s[255]; + va_list args; + va_start(args, fmt); + if (sizeof(s) < vsprintf(s, fmt, args)) + printk(MCDX ":: dprintf exeeds limit!!\n"); + else printk(MCDX ": %s", s); + va_end(args); +} + + +int mcdx_init(void) +{ + int drive; + + INFO((": Version 1.0a " + "mcdx.c,v 1.7 1995/08/27 01:46:41 heiko Exp\n")); + + /* zero the pointer array */ + for (drive = 0; drive < MCDX_NDRIVES; drive++) + mcdx_stuffp[drive] = NULL; + + /* do the initialisation */ + for (drive = 0; drive < MCDX_NDRIVES; drive++) { + struct s_version version; + struct s_drive_stuff* stuffp; + int size; + + size = sizeof(*stuffp); + + TRACE((INIT, "init() try drive %d\n", drive)); + + TRACE((MALLOC, "init() malloc %d bytes\n", size)); + if (!(stuffp = kmalloc(size, GFP_KERNEL))) { + WARN(("init() malloc failed\n")); + break; + } + + TRACE((INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp)); + + /* zero the stuff */ + memset(stuffp, 0, sizeof(*stuffp)); + + stuffp->present = 0; /* this should be 0 already */ + stuffp->toc = NULL; /* this should be NULL already */ + stuffp->changed = jiffies; + + /* setup our irq and i/o addresses */ + stuffp->irq = irq(mcdx_drive_map[drive]); + stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]); + stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1; + stuffp->wreg_hcon = stuffp->wreg_reset + 1; + stuffp->wreg_chn = stuffp->wreg_hcon + 1; + + /* check if i/o addresses are available */ + if (0 != check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) { + WARN(("%s=0x%3p,%d: " + "Init failed. I/O ports (0x%3p..0x3p) already in use.\n" + MCDX, + stuffp->wreg_data, stuffp->irq, + stuffp->wreg_data, + stuffp->wreg_data + MCDX_IO_SIZE - 1)); + TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp)); + kfree(stuffp); + TRACE((INIT, "init() continue at next drive\n")); + continue; /* next drive */ + } + + TRACE((INIT, "init() i/o port is available at 0x%3p\n", stuffp->wreg_data)); + + TRACE((INIT, "init() hardware reset\n")); + mcdx_reset(stuffp, HARD, 1); + + TRACE((INIT, "init() get version\n")); + if (-1 == mcdx_requestversion(stuffp, &version, 4)) { + /* failed, next drive */ + WARN(("%s=0x%3p,%d: Init failed. Can't get version.\n", + MCDX, + stuffp->wreg_data, stuffp->irq)); + TRACE((MALLOC, "init() free stuffp @ %p\n", stuffp)); + kfree(stuffp); + TRACE((INIT, "init() continue at next drive\n")); + continue; + } + + switch (version.code) { + case 'D': + stuffp->readcmd = READDSPEED; + stuffp->present = DOUBLE | DOOR | MULTI; + break; + case 'F': + stuffp->readcmd = READSSPEED; + stuffp->present = SINGLE | DOOR | MULTI; + break; + case 'M': + stuffp->readcmd = READSSPEED; + stuffp->present = SINGLE; + break; + default: + stuffp->present = 0; break; + } + + stuffp->playcmd = READSSPEED; + + + if (!stuffp->present) { + WARN(("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n", + MCDX, stuffp->wreg_data, stuffp->irq)); + kfree(stuffp); + continue; /* next drive */ + } + + TRACE((INIT, "init() register blkdev\n")); + if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) { + WARN(("%s=0x%3p,%d: Init failed. Can't get major %d.\n", + MCDX, + stuffp->wreg_data, stuffp->irq, MAJOR_NR)); + kfree(stuffp); + continue; /* next drive */ + } + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = READ_AHEAD; + +#if WE_KNOW_WHY + blksize_size[MAJOR_NR] = BLKSIZES; +#endif + + TRACE((INIT, "init() subscribe irq and i/o\n")); + mcdx_irq_map[stuffp->irq] = stuffp; + if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, DEVICE_NAME)) { + WARN(("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n", + MCDX, + stuffp->wreg_data, stuffp->irq, stuffp->irq)); + stuffp->irq = 0; + kfree(stuffp); + continue; + } + request_region((unsigned int) stuffp->wreg_data, + MCDX_IO_SIZE, + DEVICE_NAME); + + TRACE((INIT, "init() get garbage\n")); + { + int i; + mcdx_delay(stuffp, HZ/2); + for (i = 100; i; i--) (void) inb((unsigned int) stuffp->rreg_status); + } + + +#if WE_KNOW_WHY + outb(0x50, (unsigned int) stuffp->wreg_chn); /* irq 11 -> channel register */ +#endif + + TRACE((INIT, "init() set non dma but irq mode\n")); + mcdx_config(stuffp, 1); + + stuffp->minor = drive; + + WARN((DEVICE_NAME " installed at 0x%3p, irq %d." + " (Firmware version %c %x)\n", + stuffp->wreg_data, stuffp->irq, version.code, + version.ver)); + mcdx_stuffp[drive] = stuffp; + TRACE((INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp)); + } + + return 0; +} + + +static int mcdx_transfer(struct s_drive_stuff *stuffp, + char *p, int sector, int nr_sectors) +/* This does actually the transfer from the drive. + Return: -1 on timeout or other error + else status byte (as in stuff->st) */ +{ + int off; + int done = 0; + + TRACE((TRANSFER, "transfer() %d sectors at sector %d\n", + nr_sectors, sector)); + + if (stuffp->audio) { + WARN(("attempt to read from audio cd\n")); + return -1; + } + + while (stuffp->lock) + interruptible_sleep_on(&stuffp->lockq); + if (current->signal && ~current->blocked) { + WARN(("talk() got signal %d\n", current->signal)); + } + + if (stuffp->valid + && (sector >= stuffp->pending) + && (sector < stuffp->off_direct)) { + + + off = stuffp->off_requested < (off = sector + nr_sectors) + ? stuffp->off_requested : off; + + stuffp->lock = current->pid; + + do { + int sig = 0; + int to = 0; + + /* wait for the drive become idle */ + current->timeout = jiffies + 5*HZ; + while (stuffp->busy) { + interruptible_sleep_on(&stuffp->busyq); + if ((sig = (current->signal && ~current->blocked)) + || (to = (current->timeout == 0))) { + break; + } + } + + current->timeout = 0; + + /* test for possible errors */ + if (((stuffp->busy == 0) && !stuffp->introk) + || sig + || to) { + if ((stuffp->busy == 0) && !stuffp->introk) + WARN(("mcdx_transfer() failure in data request\n")); + else if (to) + WARN(("mcdx_transfer(): timeout\n")); + else if (sig) + WARN(("mcdx_transfer(): got signal 0x%lx\n", current->signal)); + + stuffp->lock = 0; + stuffp->busy = 0; + wake_up_interruptible(&stuffp->lockq); + wake_up_interruptible(&stuffp->busyq); + stuffp->errno = MCDX_E; + TRACE((TRANSFER, "transfer() done (-1)\n")); + return -1; + } + + /* test if it's the first sector of a block, + * there we have to skip some bytes as we read raw data */ + if (stuffp->xa && (0 == (stuffp->pending & 3))) { + const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE; + TRACE((TRANSFER, "transfer() sector %d, skip %d header bytes\n", + stuffp->pending, HEAD)); + insb((unsigned int) stuffp->rreg_data, p, HEAD); + } + + /* now actually read the data */ + + TRACE((TRANSFER, "transfer() read sector %d\n", stuffp->pending)); + insb((unsigned int) stuffp->rreg_data, p, 512); + + /* test if it's the last sector of a block, + * if so, we have to expect an interrupt and to skip some + * data too */ + if ((stuffp->busy = (3 == (stuffp->pending & 3))) && stuffp->xa) { + char dummy[CD_XA_TAIL]; + TRACE((TRANSFER, "transfer() sector %d, skip %d trailer bytes\n", + stuffp->pending, CD_XA_TAIL)); + insb((unsigned int) stuffp->rreg_data, &dummy[0], CD_XA_TAIL); + } + + if (stuffp->pending == sector) { + p += 512; + done++; + sector++; + } + } + while (++(stuffp->pending) < off); + + stuffp->lock = 0; + wake_up_interruptible(&stuffp->lockq); + + } else { + + static unsigned char cmd[] = { + 0, + 0, 0, 0, + 0, 0, 0 + }; + + cmd[0] = stuffp->readcmd; + + stuffp->valid = 1; + stuffp->pending = sector & ~3; + + /* do some sanity checks */ + TRACE((TRANSFER, "transfer() request sector %d\n", stuffp->pending)); + if (stuffp->pending > stuffp->lastsector) { + WARN(("transfer() sector %d from nirvana requested.\n", + stuffp->pending)); + stuffp->errno = MCDX_EOM; + TRACE((TRANSFER, "transfer() done (-1)\n")); + return -1; + } + + if ((stuffp->off_direct = stuffp->pending + DIRECT_SIZE) + > stuffp->lastsector + 1) + stuffp->off_direct = stuffp->lastsector + 1; + if ((stuffp->off_requested = stuffp->pending + REQUEST_SIZE) + > stuffp->lastsector + 1) + stuffp->off_requested = stuffp->lastsector + 1; + + TRACE((TRANSFER, "transfer() pending %d\n", stuffp->pending)); + TRACE((TRANSFER, "transfer() off_dir %d\n", stuffp->off_direct)); + TRACE((TRANSFER, "transfer() off_req %d\n", stuffp->off_requested)); + + { + struct s_msf pending; + log2msf(stuffp->pending / 4, &pending); + cmd[1] = pending.minute; + cmd[2] = pending.second; + cmd[3] = pending.frame; + } + + stuffp->busy = 1; + cmd[6] = (unsigned char) (stuffp->off_requested - stuffp->pending) / 4; + + outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd); + + } + + stuffp->off_direct = (stuffp->off_direct += done) < stuffp->off_requested + ? stuffp->off_direct : stuffp->off_requested; + + TRACE((TRANSFER, "transfer() done (%d)\n", done)); + return done; +} + + +/* Access to elements of the mcdx_drive_map members */ + +static char* port(int *ip) { return (char*) ip[0]; } +static int irq(int *ip) { return ip[1]; } + +/* Misc number converters */ + +static unsigned int bcd2uint(unsigned char c) +{ return (c >> 4) * 10 + (c & 0x0f); } + +static unsigned int uint2bcd(unsigned int ival) +{ return ((ival / 10) << 4) | (ival % 10); } + +static void log2msf(unsigned int l, struct s_msf* pmsf) +{ + l += CD_BLOCK_OFFSET; + pmsf->minute = uint2bcd(l / 4500), l %= 4500; + pmsf->second = uint2bcd(l / 75); + pmsf->frame = uint2bcd(l % 75); +} + +static unsigned int msf2log(const struct s_msf* pmsf) +{ + return bcd2uint(pmsf->frame) + + bcd2uint(pmsf->second) * 75 + + bcd2uint(pmsf->minute) * 4500 + - CD_BLOCK_OFFSET; +} + +int mcdx_readtoc(struct s_drive_stuff* stuffp) +/* Read the toc entries from the CD, + * Return: -1 on failure, else 0 */ +{ + + if (stuffp->toc) { + TRACE((IOCTL, "ioctl() toc already read\n")); + return 0; + } + + TRACE((IOCTL, "ioctl() readtoc for %d tracks\n", + stuffp->di.n_last - stuffp->di.n_first + 1)); + + if (-1 == mcdx_hold(stuffp, 1)) return -1; + + TRACE((IOCTL, "ioctl() tocmode\n")); + if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO; + + /* all seems to be ok so far ... malloc */ + { + int size; + size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2); + + TRACE((MALLOC, "ioctl() malloc %d bytes\n", size)); + stuffp->toc = kmalloc(size, GFP_KERNEL); + if (!stuffp->toc) { + WARN(("Cannot malloc %s bytes for toc\n", size)); + mcdx_setdrivemode(stuffp, DATA, 1); + return -EIO; + } + } + + /* now read actually the index */ + { + int trk; + int retries; + + for (trk = 0; + trk < (stuffp->di.n_last - stuffp->di.n_first + 1); + trk++) + stuffp->toc[trk].index = 0; + + for (retries = 300; retries; retries--) { /* why 300? */ + struct s_subqcode q; + unsigned int idx; + + if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) { + mcdx_setdrivemode(stuffp, DATA, 1); + return -EIO; + } + + idx = bcd2uint(q.index); + + if ((idx > 0) + && (idx <= stuffp->di.n_last) + && (q.tno == 0) + && (stuffp->toc[idx - stuffp->di.n_first].index == 0)) { + stuffp->toc[idx - stuffp->di.n_first] = q; + TRACE((IOCTL, "ioctl() toc idx %d (trk %d)\n", idx, trk)); + trk--; + } + if (trk == 0) break; + } + memset(&stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1], + 0, sizeof(stuffp->toc[0])); + stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt + = stuffp->di.msf_leadout; + } + + /* unset toc mode */ + TRACE((IOCTL, "ioctl() undo toc mode\n")); + if (-1 == mcdx_setdrivemode(stuffp, DATA, 2)) + return -EIO; + +#if MCDX_DEBUG && IOCTL + { int trk; + for (trk = 0; + trk < (stuffp->di.n_last - stuffp->di.n_first + 2); + trk++) + TRACE((IOCTL, "ioctl() %d readtoc %02x %02x %02x" + " %02x:%02x.%02x %02x:%02x.%02x\n", + trk + stuffp->di.n_first, + stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index, + stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame, + stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame)); + } +#endif + + return 0; +} + +static int +mcdx_playmsf(struct s_drive_stuff* stuffp, const struct cdrom_msf* msf) +{ + unsigned char cmd[7] = { + 0, 0, 0, 0, 0, 0, 0 + }; + + cmd[0] = stuffp->playcmd; + + cmd[1] = msf->cdmsf_min0; + cmd[2] = msf->cdmsf_sec0; + cmd[3] = msf->cdmsf_frame0; + cmd[4] = msf->cdmsf_min1; + cmd[5] = msf->cdmsf_sec1; + cmd[6] = msf->cdmsf_frame1; + + TRACE((IOCTL, "ioctl(): play %x " + "%02x:%02x:%02x -- %02x:%02x:%02x\n", + cmd[0], cmd[1], cmd[2], cmd[3], + cmd[4], cmd[5], cmd[6])); + + outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd); + + if (-1 == mcdx_getval(stuffp, 1*HZ, 0, NULL)) { + WARN(("playmsf() timeout\n")); + return -1; + } + + stuffp->audiostatus = CDROM_AUDIO_PLAY; + return 0; +} + +static int +mcdx_playtrk(struct s_drive_stuff* stuffp, const struct cdrom_ti* ti) +{ + struct s_subqcode* p; + struct cdrom_msf msf; + + if (-1 == mcdx_readtoc(stuffp)) return -1; + + if (ti) p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first]; + else p = &stuffp->start; + + msf.cdmsf_min0 = p->dt.minute; + msf.cdmsf_sec0 = p->dt.second; + msf.cdmsf_frame0 = p->dt.frame; + + if (ti) { + p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1]; + stuffp->stop = *p; + } else p = &stuffp->stop; + + msf.cdmsf_min1 = p->dt.minute; + msf.cdmsf_sec1 = p->dt.second; + msf.cdmsf_frame1 = p->dt.frame; + + return mcdx_playmsf(stuffp, &msf); +} + + +/* Drive functions ************************************************/ + +static int +mcdx_closedoor(struct s_drive_stuff *stuffp, int tries) +{ + if (stuffp->present & DOOR) + return mcdx_talk(stuffp, "\xf8", 1, NULL, 0, 5*HZ, tries); + else + return 0; +} + +static int +mcdx_stop(struct s_drive_stuff *stuffp, int tries) +{ return mcdx_talk(stuffp, "\xf0", 1, NULL, 0, 2*HZ, tries); } + +static int +mcdx_hold(struct s_drive_stuff *stuffp, int tries) +{ return mcdx_talk(stuffp, "\x70", 1, NULL, 0, 2*HZ, tries); } + +static int +mcdx_eject(struct s_drive_stuff *stuffp, int tries) +{ + if (stuffp->present & DOOR) + return mcdx_talk(stuffp, "\xf6", 1, NULL, 0, 5*HZ, tries); + else + return 0; +} + +static int +mcdx_requestsubqcode(struct s_drive_stuff *stuffp, + struct s_subqcode *sub, + int tries) +{ + char buf[11]; + int ans; + + if (-1 == (ans = mcdx_talk( + stuffp, "\x20", 1, buf, sizeof(buf), + 2*HZ, tries))) + return -1; + sub->control = buf[1]; + sub->tno = buf[2]; + sub->index = buf[3]; + sub->tt.minute = buf[4]; + sub->tt.second = buf[5]; + sub->tt.frame = buf[6]; + sub->dt.minute = buf[8]; + sub->dt.second = buf[9]; + sub->dt.frame = buf[10]; + + return ans; +} + +static int +mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, struct s_multi *multi, int tries) +{ + char buf[5]; + int ans; + + if (stuffp->present & MULTI) { + ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2*HZ, tries); + multi->multi = buf[1]; + multi->msf_last.minute = buf[2]; + multi->msf_last.second = buf[3]; + multi->msf_last.frame = buf[4]; + return ans; + } else { + multi->multi = 0; + return 0; + } +} + +static int +mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, int tries) +{ + char buf[9]; + int ans; + ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2*HZ, tries); + info->n_first = bcd2uint(buf[1]); + info->n_last = bcd2uint(buf[2]); + info->msf_leadout.minute = buf[3]; + info->msf_leadout.second = buf[4]; + info->msf_leadout.frame = buf[5]; + info->msf_first.minute = buf[6]; + info->msf_first.second = buf[7]; + info->msf_first.frame = buf[8]; + return ans; +} + +static int +mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries) +{ + char cmd[2]; + int ans; + + TRACE((HW, "setdrivemode() %d\n", mode)); + + if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5*HZ, tries))) + return -1; + + switch (mode) { + case TOC: cmd[1] |= 0x04; break; + case DATA: cmd[1] &= ~0x04; break; + case RAW: cmd[1] |= 0x40; break; + case COOKED: cmd[1] &= ~0x40; break; + default: break; + } + cmd[0] = 0x50; + return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries); +} + + +static int +mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries) +{ + unsigned char cmd[2] = { 0xa0 }; + TRACE((HW, "setdatamode() %d\n", mode)); + switch (mode) { + case MODE0: cmd[1] = 0x00; break; + case MODE1: cmd[1] = 0x01; break; + case MODE2: cmd[1] = 0x02; break; + default: return -EINVAL; + } + return mcdx_talk(stuffp, cmd, 2, NULL, 0, 5*HZ, tries); +} + +static int +mcdx_config(struct s_drive_stuff *stuffp, int tries) +{ + char cmd[4]; + + TRACE((HW, "config()\n")); + + cmd[0] = 0x90; + + cmd[1] = 0x10; /* irq enable */ + cmd[2] = 0x05; /* pre, err irq enable */ + + if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries)) + return -1; + + cmd[1] = 0x02; /* dma select */ + cmd[2] = 0x00; /* no dma */ + + return mcdx_talk(stuffp, cmd, 3, NULL, 0, 1*HZ, tries); +} + +static int +mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, int tries) +{ + char buf[3]; + int ans; + + if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 1, buf, sizeof(buf), 2*HZ, tries))) + return ans; + + ver->code = buf[1]; + ver->ver = buf[2]; + + return ans; +} + +static int +mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries) +{ + if (mode == HARD) { + outb(0, (unsigned int) stuffp->wreg_chn); /* no dma, no irq -> hardware */ + outb(0, (unsigned int) stuffp->wreg_reset); /* hw reset */ + return 0; + } else return mcdx_talk(stuffp, "\x60", 1, NULL, 0, 5*HZ, tries); +} + +static int +mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries) +{ + char cmd[2] = { 0xfe }; + if (stuffp->present & DOOR) { + cmd[1] = lock ? 0x01 : 0x00; + return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 0, 5*HZ, tries); + } else + return 0; +} + +static int +mcdx_getstatus(struct s_drive_stuff *stuffp, int tries) +{ return mcdx_talk(stuffp, "\x40", 1, NULL, 0, 5*HZ, tries); } + +static int +mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char* buf) +{ + unsigned long timeout = to + jiffies; + char c; + + if (!buf) buf = &c; + + while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) { + if (jiffies > timeout) return -1; + mcdx_delay(stuffp, delay); + } + + *buf = (unsigned char) inb((unsigned int) stuffp->rreg_data) & 0xff; + + return 0; +} diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c --- v1.3.35/linux/drivers/cdrom/optcd.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/optcd.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,1669 @@ +/* $Id: optcd.c,v 1.3 1995/08/24 19:54:27 root Exp root $ + linux/drivers/block/optcd.c - Optics Storage 8000 AT CDROM driver + + Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl) + + Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks + by Eberhard Moenkeberg (emoenke@gwdg.de). ISP16 detection and + configuration by Eric van der Maarel (maarel@marin.nl), with some data + communicated by Vadim V. Model (vadim@rbrf.msk.su). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + History + 14-5-95 v0.0 Plays sound tracks. No reading of data CDs yet. + Detection of disk change doesn't work. + 21-5-95 v0.1 First ALPHA version. CD can be mounted. The + device major nr is borrowed from the Aztech + driver. Speed is around 240 kb/s, as measured + with "time dd if=/dev/cdrom of=/dev/null \ + bs=2048 count=4096". + 24-6-95 v0.2 Reworked the #defines for the command codes + and the like, as well as the structure of + the hardware communication protocol, to + reflect the "official" documentation, kindly + supplied by C.K. Tan, Optics Storage Pte. Ltd. + Also tidied up the state machine somewhat. + 28-6-95 v0.3 Removed the ISP-16 interface code, as this + should go into its own driver. The driver now + has its own major nr. + Disk change detection now seems to work, too. + This version became part of the standard + kernel as of version 1.3.7 + 24-9-95 v0.4 Re-inserted ISP-16 interface code which I + copied from sjcd.c, with a few changes. + Updated README.optcd. Submitted for + inclusion in 1.3.21 + 29-9-95 v0.4a Fixed bug that prevented compilation as module +*/ + +#include +#include + +#ifdef MODULE +# include +# include +# 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 +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR OPTICS_CDROM_MAJOR + +# include +#define optcd_port optcd /* Needed for the modutils. */ +# include + + +/* 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 + * + * Version 0.5 + * + * Detect cdrom interface on ISP16 soundcard. + * Configure cdrom interface. + * + * Algorithm for the card with no IDE support option taken + * from the CDSETUP.SYS driver for MSDOS, + * by OPTi Computers, version 2.03. + * Algorithm for the IDE supporting ISP16 as communicated + * to me by Vadim Model and Leo Spiekman. + * + * Use, modifification or redistribution of this software is + * allowed under the terms of the GPL. + * + */ + + +#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p)) +#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p)) + +static short +isp16_detect(void) +{ + + if ( !( isp16_with_ide__detect() < 0 ) ) + return(2); + else + return( isp16_no_ide__detect() ); +} + +static short +isp16_no_ide__detect(void) +{ + u_char ctrl; + u_char enable_cdrom; + u_char io; + short i = -1; + + isp16_ctrl = ISP16_NO_IDE__CTRL; + isp16_enable_cdrom_port = ISP16_NO_IDE__ENABLE_CDROM_PORT; + + /* read' and write' are a special read and write, respectively */ + + /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */ + ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC; + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + + /* read' 3,4 and 5-bit from the cdrom enable port */ + enable_cdrom = ISP16_IN( ISP16_NO_IDE__ENABLE_CDROM_PORT ) & 0x38; + + if ( !(enable_cdrom & 0x20) ) { /* 5-bit not set */ + /* read' last 2 bits of ISP16_IO_SET_PORT */ + io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03; + if ( ((io&0x01)<<1) == (io&0x02) ) { /* bits are the same */ + if ( io == 0 ) { /* ...the same and 0 */ + i = 0; + enable_cdrom |= 0x20; + } + else { /* ...the same and 1 */ /* my card, first time 'round */ + i = 1; + enable_cdrom |= 0x28; + } + ISP16_OUT( ISP16_NO_IDE__ENABLE_CDROM_PORT, enable_cdrom ); + } + else { /* bits are not the same */ + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + return(i); /* -> not detected: possibly incorrect conclusion */ + } + } + else if ( enable_cdrom == 0x20 ) + i = 0; + else if ( enable_cdrom == 0x28 ) /* my card, already initialised */ + i = 1; + + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + + return(i); +} + +static short +isp16_with_ide__detect(void) +{ + u_char ctrl; + u_char tmp; + + isp16_ctrl = ISP16_IDE__CTRL; + isp16_enable_cdrom_port = ISP16_IDE__ENABLE_CDROM_PORT; + + /* read' and write' are a special read and write, respectively */ + + /* read' ISP16_CTRL_PORT and save */ + ctrl = ISP16_IN( ISP16_CTRL_PORT ); + + /* write' zero to the ctrl port and get response */ + ISP16_OUT( ISP16_CTRL_PORT, 0 ); + tmp = ISP16_IN( ISP16_CTRL_PORT ); + + if ( tmp != 2 ) /* isp16 with ide option not detected */ + return(-1); + + /* restore ctrl port value */ + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + + return(2); +} + +static short +isp16_config( int base, u_char drive_type, int irq, int dma ) +{ + u_char base_code; + u_char irq_code; + u_char dma_code; + u_char i; + + if ( (drive_type == ISP16_MITSUMI) && (dma != 0) ) + printk( "Mitsumi cdrom drive has no dma support.\n" ); + + switch (base) { + case 0x340: base_code = ISP16_BASE_340; break; + case 0x330: base_code = ISP16_BASE_330; break; + case 0x360: base_code = ISP16_BASE_360; break; + case 0x320: base_code = ISP16_BASE_320; break; + default: + printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base ); + return(-1); + } + switch (irq) { + case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */ + case 5: irq_code = ISP16_IRQ_5; + printk( "Irq 5 shouldn't be used by cdrom interface on ISP16," + " due to possible conflicts with the soundcard.\n"); + break; + case 7: irq_code = ISP16_IRQ_7; + printk( "Irq 7 shouldn't be used by cdrom interface on ISP16," + " due to possible conflicts with the soundcard.\n"); + break; + case 3: irq_code = ISP16_IRQ_3; break; + case 9: irq_code = ISP16_IRQ_9; break; + case 10: irq_code = ISP16_IRQ_10; break; + case 11: irq_code = ISP16_IRQ_11; break; + default: + printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq ); + return(-1); + } + switch (dma) { + case 0: dma_code = ISP16_DMA_X; break; /* disable dma */ + case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16," + " due to conflict with the soundcard.\n"); + return(-1); break; + case 3: dma_code = ISP16_DMA_3; break; + case 5: dma_code = ISP16_DMA_5; break; + case 6: dma_code = ISP16_DMA_6; break; + case 7: dma_code = ISP16_DMA_7; break; + default: + printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma ); + return(-1); + } + + if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 && + drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 && + drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI && + drive_type != ISP16_DRIVE_X ) { + printk( "Drive type (code 0x%02X) not supported by cdrom" + " interface on ISP16.\n", drive_type ); + return(-1); + } + + /* set type of interface */ + i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK; /* clear some bits */ + ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type ); + + /* enable cdrom on interface with ide support */ + if ( isp16_type > 1 ) + ISP16_OUT( isp16_enable_cdrom_port, ISP16_ENABLE_CDROM ); + + /* set base address, irq and dma */ + i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK; /* keep some bits */ + ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code ); + + return(0); +} diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c --- v1.3.35/linux/drivers/cdrom/sbpcd.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/sbpcd.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,5422 @@ +/* + * sbpcd.c CD-ROM device driver for the whole family of IDE-style + * Kotobuki/Matsushita/Panasonic CR-5xx drives for + * SoundBlaster ("Pro" or "16 ASP" or compatible) cards + * and for "no-sound" interfaces like Lasermate and the + * Panasonic CI-101P. + * Also for the Longshine LCS-7260 drive. + * Also for the IBM "External ISA CD-Rom" drive. + * Also for the CreativeLabs CD200 drive (but I still need some + * detailed bug reports). + * Also for the TEAC CD-55A drive. + * Not for Sanyo drives (but sjcd is there...). + * Not for any other Funai drives than E2550UA (="CD200" with "F"). + * + * NOTE: This is release 3.9. + * + * VERSION HISTORY + * + * 0.1 initial release, April/May 93, after mcd.c (Martin Harriss) + * + * 0.2 the "repeat:"-loop in do_sbpcd_request did not check for + * end-of-request_queue (resulting in kernel panic). + * Flow control seems stable, but throughput is not better. + * + * 0.3 interrupt locking totally eliminated (maybe "inb" and "outb" + * are still locking) - 0.2 made keyboard-type-ahead losses. + * check_sbpcd_media_change added (to use by isofs/inode.c) + * - but it detects almost nothing. + * + * 0.4 use MAJOR 25 definitely. + * Almost total re-design to support double-speed drives and + * "naked" (no sound) interface cards ("LaserMate" interface type). + * Flow control should be exact now. + * Don't occupy the SbPro IRQ line (not needed either); will + * live together with Hannu Savolainen's sndkit now. + * Speeded up data transfer to 150 kB/sec, with help from Kai + * Makisara, the "provider" of the "mt" tape utility. + * Give "SpinUp" command if necessary. + * First steps to support up to 4 drives (but currently only one). + * Implemented audio capabilities - workman should work, xcdplayer + * gives some problems. + * This version is still consuming too much CPU time, and + * sleeping still has to be worked on. + * During "long" implied seeks, it seems possible that a + * ReadStatus command gets ignored. That gives the message + * "ResponseStatus timed out" (happens about 6 times here during + * a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is + * handled without data error, but it should get done better. + * + * 0.5 Free CPU during waits (again with help from Kai Makisara). + * Made it work together with the LILO/kernel setup standard. + * Included auto-probing code, as suggested by YGGDRASIL. + * Formal redesign to add DDI debugging. + * There are still flaws in IOCTL (workman with double speed drive). + * + * 1.0 Added support for all drive IDs (0...3, no longer only 0) + * and up to 4 drives on one controller. + * Added "#define MANY_SESSION" for "old" multi session CDs. + * + * 1.1 Do SpinUp for new drives, too. + * Revised for clean compile under "old" kernels (0.99pl9). + * + * 1.2 Found the "workman with double-speed drive" bug: use the driver's + * audio_state, not what the drive is reporting with ReadSubQ. + * + * 1.3 Minor cleanups. + * Refinements regarding Workman. + * + * 1.4 Read XA disks (PhotoCDs) with "old" drives, too (but only the first + * session - no chance to fully access a "multi-session" CD). + * This currently still is too slow (50 kB/sec) - but possibly + * the old drives won't do it faster. + * Implemented "door (un)lock" for new drives (still does not work + * as wanted - no lock possible after an unlock). + * Added some debugging printout for the UPC/EAN code - but my drives + * return only zeroes. Is there no UPC/EAN code written? + * + * 1.5 Laborate with UPC/EAN code (not better yet). + * Adapt to kernel 1.1.8 change (have to explicitly include + * 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 + * + * 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 + +#ifdef MODULE +#include +#include +#ifndef CONFIG_MODVERSIONS +char kernel_version[]=UTS_RELEASE; +#endif +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif MODULE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#define VERSION "v3.9 Eberhard Moenkeberg " + +/*==========================================================================*/ +/* + * 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<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<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;i0) msg(DBG_INF,"ResponseInfo: got %d trailing bytes.\n",j); +#endif 000 + for (j=0;j0) 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)||(timeout0;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;j1) 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_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>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_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>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;i200) + { + 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;j0;i--) OUT(port+0,0); + for (k=0;k0;i--) + { + if (inb(port+1)&s_not_result_ready) continue; + response[k]=inb(port+0); + break; + } + } + for (i=0;i0;i--) OUT(port+0,0); + for (k=0;k0;i--) + { + if (inb(port+1)&s_not_result_ready) continue; + response[k]=inb(port+0); + break; + } + } + for (i=0;i0;i--) OUT(port+0,0); + for (k=0;k0;i--) + { + if (inb(port+1)&s_not_result_ready) continue; + response[k]=inb(port+0); + break; + } + } + for (i=0;i0;i--) OUT(port+0,0); + for (k=0;k0;i--) + { + if (inb(port+1)&s_not_result_ready) continue; + response[k]=inb(port+0); + break; + } + } + for (i=0;i>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=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_trk0D_S[d].n_last_track) return (-EINVAL); + if (ti.cdti_trk1D_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 (iD_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>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= 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;j1) 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=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=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;j0) 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;j0) + vfree(D_S[j].aud_buf); + } + msg(DBG_INF, "%s module released.\n", major_name); +} +#endif MODULE +/*==========================================================================*/ +/* + * Check if the media has changed in the CD-ROM drive. + * used externally (isofs/inode.c, fs/buffer.c) + * Currently disabled (has to get "synchronized"). + */ +static int sbpcd_chk_disk_change(kdev_t full_dev) +{ + int i, st; + + msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev)); + return (0); /* "busy" test necessary before we really can check */ + + i=MINOR(full_dev); + if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) ) + { + msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev); + return (-1); + } + + switch_drive(i); + + cc_ReadStatus(); /* command: give 1-byte status */ + st=ResponseStatus(); + msg(DBG_CHK,"media_check: %02X\n",D_S[d].status_bits); + if (st<0) + { + msg(DBG_INF,"media_check: ResponseStatus error.\n"); + return (1); /* status not obtainable */ + } + if (D_S[d].CD_changed==0xFF) msg(DBG_CHK,"media_check: \"changed\" assumed.\n"); + if (!st_spinning) msg(DBG_CHK,"media_check: motor off.\n"); + if (!st_door_closed) + { + msg(DBG_CHK,"media_check: door open.\n"); + D_S[d].CD_changed=0xFF; + } + if (!st_caddy_in) + { + msg(DBG_CHK,"media_check: no disk in drive.\n"); + D_S[d].open_count=0; + D_S[d].CD_changed=0xFF; + } + if (!st_diskok) msg(DBG_CHK,"media_check: !st_diskok.\n"); + +#if 0000 + if (D_S[d].CD_changed==0xFF) + { + D_S[d].CD_changed=1; + return (1); /* driver had a change detected before */ + } +#endif 0000 /* seems to give additional errors at the moment */ + + if (!st_diskok) return (1); /* disk not o.k. */ + if (!st_caddy_in) return (1); /* disk removed */ + if (!st_door_closed) return (1); /* door open */ + return (0); +} +/*==========================================================================*/ +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * End: + */ diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sbpcd2.c linux/drivers/cdrom/sbpcd2.c --- v1.3.35/linux/drivers/cdrom/sbpcd2.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/sbpcd2.c Fri Jan 13 09:02:32 1995 @@ -0,0 +1,5 @@ +/* + * duplication of sbpcd.c for multiple interfaces + */ +#define SBPCD_ISSUE 2 +#include "sbpcd.c" diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sbpcd3.c linux/drivers/cdrom/sbpcd3.c --- v1.3.35/linux/drivers/cdrom/sbpcd3.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/sbpcd3.c Fri Jan 13 09:02:32 1995 @@ -0,0 +1,5 @@ +/* + * duplication of sbpcd.c for multiple interfaces + */ +#define SBPCD_ISSUE 3 +#include "sbpcd.c" diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sbpcd4.c linux/drivers/cdrom/sbpcd4.c --- v1.3.35/linux/drivers/cdrom/sbpcd4.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/sbpcd4.c Fri Jan 13 09:02:32 1995 @@ -0,0 +1,5 @@ +/* + * duplication of sbpcd.c for multiple interfaces + */ +#define SBPCD_ISSUE 4 +#include "sbpcd.c" diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sjcd.c linux/drivers/cdrom/sjcd.c --- v1.3.35/linux/drivers/cdrom/sjcd.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/sjcd.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,1915 @@ +/* -- sjcd.c + * + * Sanyo CD-ROM device driver implementation, Version 1.5 + * Copyright (C) 1995 Vadim V. Model + * + * model@cecmow.enet.dec.com + * vadim@rbrf.ru + * vadim@ipsun.ras.ru + * + * ISP16 detection and configuration. + * Copyright (C) 1995 Eric van der Maarel (maarel@marin.nl) + * and Vadim Model (vadim@cecmow.enet.dec.com) + * + * + * This driver is based on pre-works by Eberhard Moenkeberg (emoenke@gwdg.de); + * it was developed under use of mcd.c from Martin Harriss, with help of + * Eric van der Maarel (maarel@marin.nl). + * + * ISP16 detection and configuration by Eric van der Maarel (maarel@marin.nl). + * Sound configuration by Vadim V. Model (model@cecmow.enet.dec.com) + * + * It is planned to include these routines into sbpcd.c later - to make + * a "mixed use" on one cable possible for all kinds of drives which use + * the SoundBlaster/Panasonic style CDROM interface. But today, the + * ability to install directly from CDROM is more important than flexibility. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: + * 1.1 First public release with kernel version 1.3.7. + * Written by Vadim Model. + * 1.2 Added detection and configuration of cdrom interface + * on ISP16 soundcard. + * Allow for command line options: sjcd=,, + * 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 +#include + +#ifdef MODULE +#include +#include +#define sjcd_init init_module +#ifndef CONFIG_MODVERSIONS +char kernel_version[]= UTS_RELEASE; +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAJOR_NR SANYO_CDROM_MAJOR +#include +#include + +/* 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 + * + * Version 0.5 + * + * Detect cdrom interface on ISP16 soundcard. + * Configure cdrom interface. + * Configure sound interface. + * + * Algorithm for the card with OPTi 82C928 taken + * from the CDSETUP.SYS driver for MSDOS, + * by OPTi Computers, version 2.03. + * Algorithm for the card with OPTi 82C929 as communicated + * to me by Vadim Model and Leo Spiekman. + * + * Use, modifification or redistribution of this software is + * allowed under the terms of the GPL. + * + */ + + +#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p)) +#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p)) + +static short +isp16_detect(void) +{ + + if ( !( isp16_c929__detect() < 0 ) ) + return(2); + else + return( isp16_c928__detect() ); +} + +static short +isp16_c928__detect(void) +{ + u_char ctrl; + u_char enable_cdrom; + u_char io; + short i = -1; + + isp16_ctrl = ISP16_C928__CTRL; + isp16_enable_port = ISP16_C928__ENABLE_PORT; + + /* read' and write' are a special read and write, respectively */ + + /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */ + ctrl = ISP16_IN( ISP16_CTRL_PORT ) & 0xFC; + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + + /* read' 3,4 and 5-bit from the cdrom enable port */ + enable_cdrom = ISP16_IN( ISP16_C928__ENABLE_PORT ) & 0x38; + + if ( !(enable_cdrom & 0x20) ) { /* 5-bit not set */ + /* read' last 2 bits of ISP16_IO_SET_PORT */ + io = ISP16_IN( ISP16_IO_SET_PORT ) & 0x03; + if ( ((io&0x01)<<1) == (io&0x02) ) { /* bits are the same */ + if ( io == 0 ) { /* ...the same and 0 */ + i = 0; + enable_cdrom |= 0x20; + } + else { /* ...the same and 1 */ /* my card, first time 'round */ + i = 1; + enable_cdrom |= 0x28; + } + ISP16_OUT( ISP16_C928__ENABLE_PORT, enable_cdrom ); + } + else { /* bits are not the same */ + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + return(i); /* -> not detected: possibly incorrect conclusion */ + } + } + else if ( enable_cdrom == 0x20 ) + i = 0; + else if ( enable_cdrom == 0x28 ) /* my card, already initialised */ + i = 1; + + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + + return(i); +} + +static short +isp16_c929__detect(void) +{ + u_char ctrl; + u_char tmp; + + isp16_ctrl = ISP16_C929__CTRL; + isp16_enable_port = ISP16_C929__ENABLE_PORT; + + /* read' and write' are a special read and write, respectively */ + + /* read' ISP16_CTRL_PORT and save */ + ctrl = ISP16_IN( ISP16_CTRL_PORT ); + + /* write' zero to the ctrl port and get response */ + ISP16_OUT( ISP16_CTRL_PORT, 0 ); + tmp = ISP16_IN( ISP16_CTRL_PORT ); + + if ( tmp != 2 ) /* isp16 with 82C929 not detected */ + return(-1); + + /* restore ctrl port value */ + ISP16_OUT( ISP16_CTRL_PORT, ctrl ); + + return(2); +} + +static short +isp16_cdi_config( int base, u_char drive_type, int irq, int dma ) +{ + u_char base_code; + u_char irq_code; + u_char dma_code; + u_char i; + + if ( (drive_type == ISP16_MITSUMI) && (dma != 0) ) + printk( "Mitsumi cdrom drive has no dma support.\n" ); + + switch (base) { + case 0x340: base_code = ISP16_BASE_340; break; + case 0x330: base_code = ISP16_BASE_330; break; + case 0x360: base_code = ISP16_BASE_360; break; + case 0x320: base_code = ISP16_BASE_320; break; + default: + printk( "Base address 0x%03X not supported by cdrom interface on ISP16.\n", base ); + return(-1); + } + switch (irq) { + case 0: irq_code = ISP16_IRQ_X; break; /* disable irq */ + case 5: irq_code = ISP16_IRQ_5; + printk( "Irq 5 shouldn't be used by cdrom interface on ISP16," + " due to possible conflicts with the soundcard.\n"); + break; + case 7: irq_code = ISP16_IRQ_7; + printk( "Irq 7 shouldn't be used by cdrom interface on ISP16," + " due to possible conflicts with the soundcard.\n"); + break; + case 3: irq_code = ISP16_IRQ_3; break; + case 9: irq_code = ISP16_IRQ_9; break; + case 10: irq_code = ISP16_IRQ_10; break; + case 11: irq_code = ISP16_IRQ_11; break; + default: + printk( "Irq %d not supported by cdrom interface on ISP16.\n", irq ); + return(-1); + } + switch (dma) { + case 0: dma_code = ISP16_DMA_X; break; /* disable dma */ + case 1: printk( "Dma 1 cannot be used by cdrom interface on ISP16," + " due to conflict with the soundcard.\n"); + return(-1); break; + case 3: dma_code = ISP16_DMA_3; break; + case 5: dma_code = ISP16_DMA_5; break; + case 6: dma_code = ISP16_DMA_6; break; + case 7: dma_code = ISP16_DMA_7; break; + default: + printk( "Dma %d not supported by cdrom interface on ISP16.\n", dma ); + return(-1); + } + + if ( drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 && + drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 && + drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI && + drive_type != ISP16_DRIVE_X ) { + printk( "Drive type (code 0x%02X) not supported by cdrom" + " interface on ISP16.\n", drive_type ); + return(-1); + } + + /* set type of interface */ + i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK; /* clear some bits */ + ISP16_OUT( ISP16_DRIVE_SET_PORT, i|drive_type ); + + /* enable cdrom on interface with 82C929 chip */ + if ( isp16_type > 1 ) + ISP16_OUT( isp16_enable_port, ISP16_ENABLE_CDROM ); + + /* set base address, irq and dma */ + i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK; /* keep some bits */ + ISP16_OUT( ISP16_IO_SET_PORT, i|base_code|irq_code|dma_code ); + + return(0); +} + +static void isp16_sound_config( void ) +{ + int i; + u_char saved; + + saved = ISP16_IN( 0xF8D ) & 0x8F; + + ISP16_OUT( 0xF8D, 0x40 ); + + /* + * Now we should wait for a while... + */ + for( i = 16*1024; i--; ); + + ISP16_OUT( 0xF8D, saved ); + + ISP16_OUT( 0xF91, 0x1B ); + + for( i = 5*64*1024; i != 0; i-- ) + if( !( inb( 0x534 ) & 0x80 ) ) break; + + if( i > 0 ) { + saved = ( inb( 0x534 ) & 0xE0 ) | 0x0A; + outb( saved, 0x534 ); + + special_mask = ( inb( 0x535 ) >> 4 ) & 0x08; + + saved = ( inb( 0x534 ) & 0xE0 ) | 0x0C; + outb( saved, 0x534 ); + + switch( inb( 0x535 ) ) { + case 0x09: + case 0x0A: + special_mask |= 0x05; + break; + case 0x8A: + special_mask = 0x0F; + break; + default: + i = 0; + } + } + if ( i == 0 ) { + printk( "Strange MediaMagic, but\n" ); + } + else { + printk( "Conf:" ); + saved = inb( 0x534 ) & 0xE0; + for( i = 0; i < 16; i++ ) { + outb( 0x20 | ( u_char )i, 0x534 ); + outb( defaults[i], 0x535 ); + } + for ( i = 0; i < 16; i++ ) { + outb( 0x20 | ( u_char )i, 0x534 ); + saved = inb( 0x535 ); + printk( " %02X", saved ); + } + printk( "\n" ); + } + + ISP16_OUT( 0xF91, 0xA0 | special_mask ); + + /* + * The following have no explaination yet. + */ + ISP16_OUT( 0xF90, 0xA2 ); + ISP16_OUT( 0xF92, 0x03 ); + + /* + * Turn general sound on and set total volume. + */ + ISP16_OUT( 0xF93, 0x0A ); + +/* + outb( 0x04, 0x224 ); + saved = inb( 0x225 ); + outb( 0x04, 0x224 ); + outb( saved, 0x225 ); +*/ + +} diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c --- v1.3.35/linux/drivers/cdrom/sonycd535.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/cdrom/sonycd535.c Sat Oct 21 19:43:31 1995 @@ -0,0 +1,1703 @@ +/* + * Sony CDU-535 interface device driver + * + * This is a modified version of the CDU-31A device driver (see below). + * Changes were made using documentation for the CDU-531 (which Sony + * assures me is very similar to the 535) and partial disassembly of the + * DOS driver. I used Minyard's driver and replaced the the CDU-31A + * commands with the CDU-531 commands. This was complicated by a different + * interface protocol with the drive. The driver is still polled. + * + * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec. + * I tried polling without the sony_sleep during the data transfers but + * it did not speed things up any. + * + * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict + * with CDU-31A driver. This is the also the number from the Linux + * Device Driver Registry for the Sony Drive. Hope nobody else is using it. + * + * 1993-08-29 (rgj) remove the configuring of the interface board address + * from the top level configuration, you have to modify it in this file. + * + * 1995-01-26 Made module-capable (Joel Katz ) + * + * 1995-05-20 + * Modified to support CDU-510/515 series + * (Claudio Porfiri) + * Fixed to report verify_area() failures + * (Heiko Eissfeldt ) + * + * 1995-06-01 + * More changes to support CDU-510/515 series + * (Claudio Porfiri) + * + * 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) && (! + +#ifdef MODULE +# include +# include +# 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REALLY_SLOW_IO +#include +#include +#include + +#include + +#define MAJOR_NR CDU535_CDROM_MAJOR +# include +#define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */ +#include + +/* + * 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 + * . + */ + /* + * This part is to deal with very slow hardware. We + * try at most MAX_SPINUP_RETRY times to read the same + * block. A check for seek_and_read_N_blocks' result is + * performed; if the result is wrong, the CDROM's engine + * is restarted and the operation is tried again. + */ + /* + * 1995-06-01: The system got problems when downloading + * from Slackware CDROM, the problem seems to be: + * seek_and_read_N_blocks returns BAD_STATUS and we + * should wait for a while before retrying, so a new + * part was added to discriminate the return value from + * seek_and_read_N_blocks for the various cases. + */ + int readStatus = seek_and_read_N_blocks(params, read_size, + status, sony_buffer, (read_size * 2048)); + if (0 <= readStatus) /* Good data; common case, placed first */ + break; + if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) { + /* give up */ + if (readStatus == NO_ROOM) + printk(CDU535_MESSAGE_NAME " No room to read from CD\n"); + else + printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n", + status[0]); + sony_first_block = -1; + sony_last_block = -1; + end_request(0); + return; + } + if (readStatus == BAD_STATUS) { + /* Sleep for a while, then retry */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + RETRY_FOR_BAD_STATUS; + schedule(); + } +#if DEBUG > 0 + printk(CDU535_MESSAGE_NAME + " debug: calling spin up when reading data!\n"); +#endif + cmd[0] = SONY535_SPIN_UP; + do_sony_cmd(cmd, 1, status, NULL, 0, 0); + } + } + /* + * The data is in memory now, copy it to the buffer and advance to the + * next block to read. + */ + copyoff = block - sony_first_block; + memcpy(CURRENT->buffer, + sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512); + + block += 1; + nsect -= 1; + CURRENT->buffer += 512; + } + + end_request(1); + break; + + case WRITE: + end_request(0); + break; + + default: + panic("Unknown SONY CD cmd"); + } + } +} + + +/* + * Read the table of contents from the drive and set sony_toc_read if + * successful. + */ +static void +sony_get_toc(void) +{ + Byte status[2]; + if (!sony_toc_read) { + /* do not call check_drive_status() from here since it can call this routine */ + if (request_toc_data(status, sony_toc) < 0) + return; + sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); + sony_toc_read = 1; + } +} + + +/* + * Search for a specific track in the table of contents. track is + * passed in bcd format + */ +static int +find_track(int track) +{ + int i; + int num_tracks; + + + num_tracks = bcd_to_int(sony_toc->last_track_num) - + bcd_to_int(sony_toc->first_track_num) + 1; + for (i = 0; i < num_tracks; i++) { + if (sony_toc->tracks[i].track == track) { + return i; + } + } + + return -1; +} + +/* + * Read the subcode and put it int last_sony_subcode for future use. + */ +static int +read_subcode(void) +{ + Byte cmd = SONY535_REQUEST_SUB_Q_DATA; + Byte status[2]; + int dsc_status; + + if (check_drive_status() != 0) + return -EIO; + + if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode, + sizeof(struct s535_sony_subcode), 1)) != 0) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n", + status[0], dsc_status); + return -EIO; + } + return 0; +} + + +/* + * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If + * the drive is playing, the subchannel needs to be read (since it would be + * changing). If the drive is paused or completed, the subcode information has + * already been stored, just use that. The ioctl call wants things in decimal + * (not BCD), so all the conversions are done. + */ +static int +sony_get_subchnl_info(long arg) +{ + struct cdrom_subchnl schi; + int err; + + /* Get attention stuff */ + if (check_drive_status() != 0) + return -EIO; + + sony_get_toc(); + if (!sony_toc_read) { + return -EIO; + } + err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi); + if (err) + return err; + + memcpy_fromfs(&schi, (char *)arg, sizeof schi); + + switch (sony_audio_status) { + case CDROM_AUDIO_PLAY: + if (read_subcode() < 0) { + return -EIO; + } + break; + + case CDROM_AUDIO_PAUSED: + case CDROM_AUDIO_COMPLETED: + break; + + case CDROM_AUDIO_NO_STATUS: + schi.cdsc_audiostatus = sony_audio_status; + memcpy_tofs((char *)arg, &schi, sizeof schi); + return 0; + break; + + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_ERROR: + default: + return -EIO; + } + + schi.cdsc_audiostatus = sony_audio_status; + schi.cdsc_adr = last_sony_subcode->address; + schi.cdsc_ctrl = last_sony_subcode->control; + schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num); + schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num); + if (schi.cdsc_format == CDROM_MSF) { + schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]); + schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]); + schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]); + + schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]); + schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]); + schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]); + } else if (schi.cdsc_format == CDROM_LBA) { + schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf); + schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf); + } + memcpy_tofs((char *)arg, &schi, sizeof schi); + return 0; +} + + +/* + * The big ugly ioctl handler. + */ +static int +cdu_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned int dev; + Byte status[2]; + Byte cmd_buff[10], params[10]; + int i; + int dsc_status; + int err; + + if (!inode) { + return -EINVAL; + } + dev = MINOR(inode->i_rdev) >> 6; + if (dev != 0) { + return -EINVAL; + } + if (check_drive_status() != 0) + return -EIO; + + switch (cmd) { + case CDROMSTART: /* Spin up the drive */ + if (spin_up_drive(status) < 0) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n", + status[0]); + return -EIO; + } + return 0; + break; + + case CDROMSTOP: /* Spin down the drive */ + cmd_buff[0] = SONY535_HOLD; + do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); + + /* + * Spin the drive down, ignoring the error if the disk was + * already not spinning. + */ + sony_audio_status = CDROM_AUDIO_NO_STATUS; + cmd_buff[0] = SONY535_SPIN_DOWN; + dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); + if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) || + ((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n", + status[0]); + return -EIO; + } + return 0; + break; + + case CDROMPAUSE: /* Pause the drive */ + cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */ + if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n", + status[0]); + return -EIO; + } + /* Get the current position and save it for resuming */ + if (read_subcode() < 0) { + return -EIO; + } + cur_pos_msf[0] = last_sony_subcode->abs_msf[0]; + cur_pos_msf[1] = last_sony_subcode->abs_msf[1]; + cur_pos_msf[2] = last_sony_subcode->abs_msf[2]; + sony_audio_status = CDROM_AUDIO_PAUSED; + return 0; + break; + + case CDROMRESUME: /* Start the drive after being paused */ + set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); + + if (sony_audio_status != CDROM_AUDIO_PAUSED) { + return -EINVAL; + } + spin_up_drive(status); + + /* Start the drive at the saved position. */ + cmd_buff[0] = SONY535_PLAY_AUDIO; + cmd_buff[1] = 0; /* play back starting at this address */ + cmd_buff[2] = cur_pos_msf[0]; + cmd_buff[3] = cur_pos_msf[1]; + cmd_buff[4] = cur_pos_msf[2]; + cmd_buff[5] = SONY535_PLAY_AUDIO; + cmd_buff[6] = 2; /* set ending address */ + cmd_buff[7] = final_pos_msf[0]; + cmd_buff[8] = final_pos_msf[1]; + cmd_buff[9] = final_pos_msf[2]; + if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || + (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n", + status[0]); + return -EIO; + } + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + break; + + case CDROMPLAYMSF: /* Play starting at the given MSF address. */ + err = verify_area(VERIFY_READ, (char *)arg, 6); + if (err) + return err; + spin_up_drive(status); + set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); + memcpy_fromfs(params, (void *)arg, 6); + + /* The parameters are given in int, must be converted */ + for (i = 0; i < 3; i++) { + cmd_buff[2 + i] = int_to_bcd(params[i]); + cmd_buff[7 + i] = int_to_bcd(params[i + 3]); + } + cmd_buff[0] = SONY535_PLAY_AUDIO; + cmd_buff[1] = 0; /* play back starting at this address */ + /* cmd_buff[2-4] are filled in for loop above */ + cmd_buff[5] = SONY535_PLAY_AUDIO; + cmd_buff[6] = 2; /* set ending address */ + /* cmd_buff[7-9] are filled in for loop above */ + if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || + (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n", + status[0]); + return -EIO; + } + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = cmd_buff[7]; + final_pos_msf[1] = cmd_buff[8]; + final_pos_msf[2] = cmd_buff[9]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + break; + + case CDROMREADTOCHDR: /* Read the table of contents header */ + { + struct cdrom_tochdr *hdr; + struct cdrom_tochdr loc_hdr; + + sony_get_toc(); + if (!sony_toc_read) + return -EIO; + hdr = (struct cdrom_tochdr *)arg; + err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr); + if (err) + return err; + loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); + loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); + memcpy_tofs(hdr, &loc_hdr, sizeof *hdr); + } + return 0; + break; + + case CDROMREADTOCENTRY: /* Read a given table of contents entry */ + { + struct cdrom_tocentry *entry; + struct cdrom_tocentry loc_entry; + int track_idx; + Byte *msf_val = NULL; + + sony_get_toc(); + if (!sony_toc_read) { + return -EIO; + } + entry = (struct cdrom_tocentry *)arg; + err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry); + if (err) + return err; + + memcpy_fromfs(&loc_entry, entry, sizeof loc_entry); + + /* Lead out is handled separately since it is special. */ + if (loc_entry.cdte_track == CDROM_LEADOUT) { + loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ; + loc_entry.cdte_ctrl = sony_toc->control2; + msf_val = sony_toc->lead_out_start_msf; + } else { + track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); + if (track_idx < 0) + return -EINVAL; + loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ; + loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; + msf_val = sony_toc->tracks[track_idx].track_start_msf; + } + + /* Logical buffer address or MSF format requested? */ + if (loc_entry.cdte_format == CDROM_LBA) { + loc_entry.cdte_addr.lba = msf_to_log(msf_val); + } else if (loc_entry.cdte_format == CDROM_MSF) { + loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); + loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1)); + loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2)); + } + memcpy_tofs(entry, &loc_entry, sizeof *entry); + } + return 0; + break; + + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + { + struct cdrom_ti ti; + int track_idx; + + sony_get_toc(); + if (!sony_toc_read) + return -EIO; + err = verify_area(VERIFY_READ, (char *)arg, sizeof ti); + if (err) + return err; + + memcpy_fromfs(&ti, (char *)arg, sizeof ti); + if ((ti.cdti_trk0 < sony_toc->first_track_num) + || (sony_toc->last_track_num < ti.cdti_trk0) + || (ti.cdti_trk1 < ti.cdti_trk0)) { + return -EINVAL; + } + track_idx = find_track(int_to_bcd(ti.cdti_trk0)); + if (track_idx < 0) + return -EINVAL; + params[1] = sony_toc->tracks[track_idx].track_start_msf[0]; + params[2] = sony_toc->tracks[track_idx].track_start_msf[1]; + params[3] = sony_toc->tracks[track_idx].track_start_msf[2]; + /* + * If we want to stop after the last track, use the lead-out + * MSF to do that. + */ + if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) { + log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1, + &(params[4])); + } else { + track_idx = find_track(int_to_bcd(ti.cdti_trk1 + 1)); + if (track_idx < 0) + return -EINVAL; + log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf) - 1, + &(params[4])); + } + params[0] = 0x03; + + spin_up_drive(status); + + set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); + + /* Start the drive at the saved position. */ + cmd_buff[0] = SONY535_PLAY_AUDIO; + cmd_buff[1] = 0; /* play back starting at this address */ + cmd_buff[2] = params[1]; + cmd_buff[3] = params[2]; + cmd_buff[4] = params[3]; + cmd_buff[5] = SONY535_PLAY_AUDIO; + cmd_buff[6] = 2; /* set ending address */ + cmd_buff[7] = params[4]; + cmd_buff[8] = params[5]; + cmd_buff[9] = params[6]; + if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || + (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n", + status[0]); + printk("... Params: %x %x %x %x %x %x %x\n", + params[0], params[1], params[2], + params[3], params[4], params[5], params[6]); + return -EIO; + } + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = params[4]; + final_pos_msf[1] = params[5]; + final_pos_msf[2] = params[6]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + } + + case CDROMSUBCHNL: /* Get subchannel info */ + return sony_get_subchnl_info(arg); + + case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ + { + struct cdrom_volctrl volctrl; + + err = verify_area(VERIFY_READ, (char *)arg, sizeof volctrl); + if (err) + return err; + + memcpy_fromfs(&volctrl, (char *)arg, sizeof volctrl); + cmd_buff[0] = SONY535_SET_VOLUME; + cmd_buff[1] = volctrl.channel0; + cmd_buff[2] = volctrl.channel1; + if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n", + status[0]); + return -EIO; + } + } + return 0; + + case CDROMEJECT: /* Eject the drive */ + cmd_buff[0] = SONY535_STOP; + do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); + cmd_buff[0] = SONY535_SPIN_DOWN; + do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); + + sony_audio_status = CDROM_AUDIO_INVALID; + cmd_buff[0] = SONY535_EJECT_CADDY; + if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n", + status[0]); + return -EIO; + } + return 0; + break; + + default: + return -EINVAL; + } +} + + +/* + * Open the drive for operations. Spin the drive up and read the table of + * contents if these have not already been done. + */ +static int +cdu_open(struct inode *inode, + struct file *filp) +{ + Byte status[2], cmd_buff[2]; + + + if (sony_inuse) + return -EBUSY; + if (check_drive_status() != 0) + return -EIO; + sony_inuse = 1; + MOD_INC_USE_COUNT; + + if (spin_up_drive(status) != 0) { + printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n", + status[0]); + sony_inuse = 0; + MOD_DEC_USE_COUNT; + return -EIO; + } + sony_get_toc(); + if (!sony_toc_read) { + cmd_buff[0] = SONY535_SPIN_DOWN; + do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); + sony_inuse = 0; + MOD_DEC_USE_COUNT; + return -EIO; + } + if (inode) { + check_disk_change(inode->i_rdev); + } + sony_usage++; + +#ifdef LOCK_DOORS + /* disable the eject button while mounted */ + cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON; + do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); +#endif + + return 0; +} + + +/* + * Close the drive. Spin it down if no task is using it. The spin + * down will fail if playing audio, so audio play is OK. + */ +static void +cdu_release(struct inode *inode, + struct file *filp) +{ + Byte status[2], cmd_no; + + sony_inuse = 0; + MOD_DEC_USE_COUNT; + + if (0 < sony_usage) { + sony_usage--; + } + if (sony_usage == 0) { + sync_dev(inode->i_rdev); + check_drive_status(); + + if (sony_audio_status != CDROM_AUDIO_PLAY) { + cmd_no = SONY535_SPIN_DOWN; + do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); + } +#ifdef LOCK_DOORS + /* enable the eject button after umount */ + cmd_no = SONY535_ENABLE_EJECT_BUTTON; + do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); +#endif + } +} + + +static struct file_operations cdu_fops = +{ + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + cdu_ioctl, /* ioctl */ + NULL, /* mmap */ + cdu_open, /* open */ + cdu_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + cdu535_check_media_change, /* check media change */ + NULL /* revalidate */ +}; + +/* + * Initialize the driver. + */ +int +sony535_init(void) +{ + struct s535_sony_drive_config drive_config; + Byte cmd_buff[3]; + Byte ret_buff[2]; + Byte status[2]; + int retry_count; + int tmp_irq; + int i; + + /* Setting the base I/O address to 0 will disable it. */ + if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0)) + return 0; + + /* Set up all the register locations */ + result_reg = sony535_cd_base_io; + command_reg = sony535_cd_base_io; + data_reg = sony535_cd_base_io + 1; + read_status_reg = sony535_cd_base_io + 2; + select_unit_reg = sony535_cd_base_io + 3; + +#ifndef USE_IRQ + sony535_irq_used = 0; /* polling only until this is ready... */ +#endif + /* we need to poll until things get initialized */ + tmp_irq = sony535_irq_used; + sony535_irq_used = 0; + +#if DEBUG > 0 + printk(CDU535_MESSAGE_NAME ": probing base address %03X\n", + sony535_cd_base_io); +#endif + if (check_region(sony535_cd_base_io,4)) { + printk(CDU535_MESSAGE_NAME ": my base address is not free!\n"); + return -EIO; + } + /* look for the CD-ROM, follows the procedure in the DOS driver */ + inb(select_unit_reg); + retry_count = jiffies + 2 * HZ; + while (jiffies < retry_count) + sony_sleep(); /* wait for 40 18 Hz ticks (from DOS driver) */ + inb(result_reg); + + outb(0, read_status_reg); /* does a reset? */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while (jiffies < retry_count) { + select_unit(0); + if (inb(result_reg) != 0xff) + break; + sony_sleep(); + } + + if ((jiffies < retry_count) && (check_drive_status() != TIME_OUT)) { + /* CD-ROM drive responded -- get the drive configuration */ + cmd_buff[0] = SONY535_INQUIRY; + if (do_sony_cmd(cmd_buff, 1, status, + (Byte *)&drive_config, 28, 1) == 0) { + /* was able to get the configuration, + * set drive mode as rest of init + */ +#if DEBUG > 0 + /* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */ + if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 ) + printk(CDU535_MESSAGE_NAME + "Inquiry command returned status = 0x%x\n", status[0]); +#endif + /* now ready to use interrupts, if available */ + sony535_irq_used = tmp_irq; +#ifndef MODULE +/* This code is not in MODULEs by default, since the autoirq stuff might + * not be in the module-accessible symbol table. + */ + /* A negative sony535_irq_used will attempt an autoirq. */ + if (sony535_irq_used < 0) { + autoirq_setup(0); + enable_interrupts(); + outb(0, read_status_reg); /* does a reset? */ + sony535_irq_used = autoirq_report(10); + disable_interrupts(); + } +#endif + if (sony535_irq_used > 0) { + if (request_irq(sony535_irq_used, cdu535_interrupt, + SA_INTERRUPT, CDU535_HANDLE)) { + printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME + " driver; polling instead.\n", sony535_irq_used); + sony535_irq_used = 0; + } + } + cmd_buff[0] = SONY535_SET_DRIVE_MODE; + cmd_buff[1] = 0x0; /* default audio */ + if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) { + /* set the drive mode successful, we are set! */ + sony_buffer_size = SONY535_BUFFER_SIZE; + sony_buffer_sectors = sony_buffer_size / 2048; + + printk(CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s", + drive_config.vendor_id, + drive_config.product_id, + drive_config.product_rev_level); + printk(" base address %03X, ", sony535_cd_base_io); + if (tmp_irq > 0) + printk("IRQ%d, ", tmp_irq); + printk("using %d byte buffer\n", sony_buffer_size); + + if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) { + printk("Unable to get major %d for %s\n", + MAJOR_NR, CDU535_MESSAGE_NAME); + return -EIO; + } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ + + sony_toc = (struct s535_sony_toc *) + kmalloc(sizeof *sony_toc, GFP_KERNEL); + if (sony_toc == NULL) + return -ENOMEM; + last_sony_subcode = (struct s535_sony_subcode *) + kmalloc(sizeof *last_sony_subcode, GFP_KERNEL); + if (last_sony_subcode == NULL) { + kfree(sony_toc); + return -ENOMEM; + } + sony_buffer = (Byte **) + kmalloc(4 * sony_buffer_sectors, GFP_KERNEL); + if (sony_buffer == NULL) { + kfree(sony_toc); + kfree(last_sony_subcode); + return -ENOMEM; + } + for (i = 0; i < sony_buffer_sectors; i++) { + sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL); + if (sony_buffer[i] == NULL) { + while (--i>=0) + kfree(sony_buffer[i]); + kfree(sony_buffer); + kfree(sony_toc); + kfree(last_sony_subcode); + return -ENOMEM; + } + } + initialized = 1; + } + } + } + + if (!initialized) { + printk("Did not find a " CDU535_MESSAGE_NAME " drive\n"); + return -EIO; + } + request_region(sony535_cd_base_io, 4, CDU535_HANDLE); + return 0; +} + +#ifndef MODULE +/* + * accept "kernel command line" parameters + * (added by emoenke@gwdg.de) + * + * use: tell LILO: + * sonycd535=0x320 + * + * the address value has to be the existing CDROM port address. + */ +void +sonycd535_setup(char *strings, int *ints) +{ + /* if IRQ change and default io base desired, + * then call with io base of 0 + */ + if (ints[0] > 0) + if (ints[0] != 0) + sony535_cd_base_io = ints[1]; + if (ints[0] > 1) + sony535_irq_used = ints[2]; + if ((strings != NULL) && (*strings != '\0')) + printk(CDU535_MESSAGE_NAME + ": Warning: Unknown interface type: %s\n", strings); +} + +#else /* MODULE */ + +void +cleanup_module(void) +{ + int i; + if (MOD_IN_USE) { + printk(CDU535_HANDLE " module in use, cannot remove\n"); + return; + } + release_region(sony535_cd_base_io, 4); + for (i = 0; i < sony_buffer_sectors; i++) + kfree_s(sony_buffer[i], 2048); + kfree_s(sony_buffer, 4 * sony_buffer_sectors); + kfree_s(last_sony_subcode, sizeof *last_sony_subcode); + kfree_s(sony_toc, sizeof *sony_toc); + if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL) + printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n"); + else + printk(CDU535_HANDLE " module released\n"); +} +#endif /* MODULE */ diff -u --recursive --new-file v1.3.35/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v1.3.35/linux/drivers/char/Config.in Thu Jan 1 02:00:00 1970 +++ linux/drivers/char/Config.in Sun Oct 22 16:03:27 1995 @@ -0,0 +1,31 @@ +# +# Character device configuration +# +mainmenu_option next_comment +comment 'character devices' + +bool 'Cyclades async mux support' CONFIG_CYCLADES +bool 'Stallion multiport serial support' CONFIG_STALDRV +if [ "$CONFIG_STALDRV" = "y" ]; then + tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION + tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION +fi +tristate 'Parallel printer support' CONFIG_PRINTER +tristate 'Logitech busmouse support' CONFIG_BUSMOUSE +tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE +if [ "$CONFIG_PSMOUSE" = "y" ]; then + bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE +fi +tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE +tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE + +bool 'QIC-02 tape support' CONFIG_QIC02_TAPE +if [ "$CONFIG_QIC02_TAPE" = "y" ]; then + bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF + if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then + comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!' + else + comment '>>> Setting runtime QIC-02 configuration is done with qic02conf' + comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/' +fi +fi diff -u --recursive --new-file v1.3.35/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v1.3.35/linux/drivers/net/Config.in Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/Config.in Sun Oct 22 16:03:27 1995 @@ -0,0 +1,85 @@ +# +# Network device configuration +# +tristate 'Dummy net driver support' CONFIG_DUMMY +tristate 'SLIP (serial line) support' CONFIG_SLIP +if [ "$CONFIG_SLIP" != "n" ]; then + bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED +fi +tristate 'PPP (point-to-point) support' CONFIG_PPP +if [ ! "$CONFIG_PPP" = "n" ]; then + comment 'CCP compressors for PPP are only built as modules.' +fi +if [ "$CONFIG_AX25" = "y" ]; then + bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC +else + bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC +fi +tristate 'PLIP (parallel port) support' CONFIG_PLIP +tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER +bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA +bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC +if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then + tristate 'WD80*3 support' CONFIG_WD80x3 + tristate 'SMC Ultra support' CONFIG_ULTRA +fi +bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE +bool '3COM cards' CONFIG_NET_VENDOR_3COM +if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then + tristate '3c501 support' CONFIG_EL1 + tristate '3c503 support' CONFIG_EL2 + if [ "$CONFIG_NET_ALPHA" = "y" ]; then + tristate '3c505 support' CONFIG_ELPLUS + tristate '3c507 support' CONFIG_EL16 + fi + tristate '3c509/3c579 support' CONFIG_EL3 +fi +bool 'Other ISA cards' CONFIG_NET_ISA +if [ "$CONFIG_NET_ISA" = "y" ]; then + tristate 'Cabletron E21xx support' CONFIG_E2100 + tristate 'DEPCA support' CONFIG_DEPCA + tristate 'EtherWorks 3 support' CONFIG_EWRK3 + if [ "$CONFIG_NET_ALPHA" = "y" ]; then + bool 'SEEQ8005 support' CONFIG_SEEQ8005 + tristate 'AT1700 support' CONFIG_AT1700 + tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO + tristate 'EtherExpress support' CONFIG_EEXPRESS + bool 'NI5210 support' CONFIG_NI52 + bool 'NI6510 support' CONFIG_NI65 + tristate 'WaveLAN support' CONFIG_WAVELAN + fi + tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS + tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN + tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 + tristate 'NE2000/NE1000 support' CONFIG_NE2000 + if [ "$CONFIG_AX25" = "y" ]; then + bool 'Ottawa PI and PI/2 support' CONFIG_PI + fi + bool 'SK_G16 support' CONFIG_SK_G16 +fi +bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA +if [ "$CONFIG_NET_EISA" = "y" ]; then + if [ "$CONFIG_NET_ALPHA" = "y" ]; then + tristate 'Ansel Communications EISA 3200 support' CONFIG_AC3200 + fi + tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT + tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5 +# tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP +# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 +# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 + bool 'Zenith Z-Note support' CONFIG_ZNET +fi +bool 'Pocket and portable adaptors' CONFIG_NET_POCKET +if [ "$CONFIG_NET_POCKET" = "y" ]; then + bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP + tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600 + tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620 +# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA +# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN +# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 +fi +bool 'Token Ring driver support' CONFIG_TR +if [ "$CONFIG_TR" = "y" ]; then + tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR +fi +tristate 'Arcnet support' CONFIG_ARCNET diff -u --recursive --new-file v1.3.35/linux/drivers/net/eql.c linux/drivers/net/eql.c --- v1.3.35/linux/drivers/net/eql.c Wed Oct 4 14:14:30 1995 +++ linux/drivers/net/eql.c Mon Oct 23 08:51:10 1995 @@ -854,26 +854,26 @@ eql_remove_slave(slave_queue_t *queue, slave_t *slave) { slave_t *prev; - slave_t *current; + slave_t *curr; cli (); prev = queue->head; - current = queue->head->next; - while (current != slave && - current->dev != 0 ) + curr = queue->head->next; + while (curr != slave && + curr->dev != 0 ) { /* printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */ - prev = current; - current = current->next; + prev = curr; + curr = curr->next; } - if (current == slave) + if (curr == slave) { - prev->next = current->next; + prev->next = curr->next; queue->num_slaves--; sti(); - return current; + return curr; } sti (); @@ -914,7 +914,7 @@ eql_remove_slave_dev(slave_queue_t *queue, struct device *dev) { slave_t *prev; - slave_t *current; + slave_t *curr; slave_t *target; target = eql_find_slave_dev (queue, dev); @@ -924,18 +924,18 @@ cli (); prev = queue->head; - current = prev->next; - while (current != target) + curr = prev->next; + while (curr != target) { - prev = current; - current = current->next; + prev = curr; + curr = curr->next; } - prev->next = current->next; + prev->next = curr->next; queue->num_slaves--; sti (); - eql_delete_slave (current); + eql_delete_slave (curr); return 0; } return 1; diff -u --recursive --new-file v1.3.35/linux/drivers/net/ppp.c linux/drivers/net/ppp.c --- v1.3.35/linux/drivers/net/ppp.c Mon Oct 16 18:38:23 1995 +++ linux/drivers/net/ppp.c Fri Oct 20 10:10:39 1995 @@ -6,7 +6,7 @@ * Dynamic PPP devices by Jim Freeman . * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid * - * ==FILEVERSION 4== + * ==FILEVERSION 6== * * NOTE TO MAINTAINERS: * If you modify this file at all, increment the number above. @@ -70,9 +70,9 @@ #include #ifdef MODULE #include -#define STATIC +#define STATIC #else -#define STATIC static +#define STATIC static #endif /* def MODULE */ #include @@ -554,9 +554,11 @@ if (first_time) { static struct symbol_table ppp_syms = { #include - 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 }; diff -u --recursive --new-file v1.3.35/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v1.3.35/linux/drivers/pci/pci.c Wed Sep 27 15:59:58 1995 +++ linux/drivers/pci/pci.c Mon Oct 23 13:21:52 1995 @@ -59,7 +59,7 @@ DEVICE( TSENG, TSENG_W32P_d, "ET4000W32P rev D"), DEVICE( WEITEK, WEITEK_P9000, "P9000"), DEVICE( WEITEK, WEITEK_P9100, "P9100"), - DEVICE( DEC, DEC_BRD, "DC21050"), + BRIDGE( DEC, DEC_BRD, "DC21050", 0x00), DEVICE( DEC, DEC_TULIP, "DC21040"), DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), DEVICE( DEC, DEC_FDDI, "DEFPA"), @@ -161,6 +161,7 @@ DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_294x, "294x"), DEVICE( ADAPTEC, ADAPTEC_2940, "2940"), + DEVICE( ADAPTEC, ADAPTEC_7872, "AIC-7872"), DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"), DEVICE( HER, HER_STING, "Stingray"), DEVICE( HER, HER_STINGARK, "Stingray ARK 2000PV") diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c --- v1.3.35/linux/drivers/scsi/53c7,8xx.c Wed Oct 4 14:14:32 1995 +++ linux/drivers/scsi/53c7,8xx.c Sat Oct 21 19:43:31 1995 @@ -172,7 +172,7 @@ #include #include #include -#include "../block/blk.h" +#include #include "scsi.h" #include "hosts.h" #include "53c7,8xx.h" diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v1.3.35/linux/drivers/scsi/Config.in Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/Config.in Sun Oct 22 16:02:13 1995 @@ -0,0 +1,39 @@ +comment 'SCSI support type (disk, tape, CDrom)' + +dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI +dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI +dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI +dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI + +comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' + +bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN + +bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS + +mainmenu_option next_comment +comment 'SCSI low-level drivers' + +dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X $CONFIG_SCSI +dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI +dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI +dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI +dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI +dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI +dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI +dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI +dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI +bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 +if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI +fi +dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 $CONFIG_SCSI +bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 +dep_tristate 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC $CONFIG_SCSI +dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI +bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 +dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI +dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI +dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI +dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI +#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/NCR53c406a.c linux/drivers/scsi/NCR53c406a.c --- v1.3.35/linux/drivers/scsi/NCR53c406a.c Wed Sep 27 15:59:58 1995 +++ linux/drivers/scsi/NCR53c406a.c Sat Oct 21 19:43:31 1995 @@ -51,7 +51,7 @@ #include #include -#include "../block/blk.h" +#include #include "scsi.h" #include "hosts.h" #include "sd.h" diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c --- v1.3.35/linux/drivers/scsi/aha152x.c Wed Sep 27 15:59:59 1995 +++ linux/drivers/scsi/aha152x.c Sat Oct 21 19:43:31 1995 @@ -215,7 +215,7 @@ #include #include -#include "../block/blk.h" +#include #include "scsi.h" #include "sd.h" #include "hosts.h" diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h --- v1.3.35/linux/drivers/scsi/aha152x.h Mon Sep 18 14:53:55 1995 +++ linux/drivers/scsi/aha152x.h Sat Oct 21 19:51:03 1995 @@ -7,7 +7,7 @@ #if defined(__KERNEL__) -#include "../block/blk.h" +#include #include "scsi.h" #include diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c --- v1.3.35/linux/drivers/scsi/aha1542.c Mon Oct 16 18:38:23 1995 +++ linux/drivers/scsi/aha1542.c Sat Oct 21 19:43:31 1995 @@ -31,7 +31,7 @@ #include #include #include -#include "../block/blk.h" +#include #include "scsi.h" #include "hosts.h" diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aha1740.c linux/drivers/scsi/aha1740.c --- v1.3.35/linux/drivers/scsi/aha1740.c Mon Sep 18 14:53:55 1995 +++ linux/drivers/scsi/aha1740.c Sat Oct 21 19:43:31 1995 @@ -32,7 +32,7 @@ #include #include -#include "../block/blk.h" +#include #include "scsi.h" #include "hosts.h" #include "sd.h" diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- v1.3.35/linux/drivers/scsi/aic7xxx.c Mon Sep 18 14:53:56 1995 +++ linux/drivers/scsi/aic7xxx.c Mon Oct 23 13:21:53 1995 @@ -41,7 +41,7 @@ * * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 04/03/95 * - * $Id: aic7xxx.c,v 2.0 1995/08/02 05:28:42 deang Exp $ + * $Id: aic7xxx.c,v 2.5 1995/09/20 05:18:18 deang Exp $ *-M*************************************************************************/ #ifdef MODULE @@ -59,7 +59,7 @@ #include #include #include -#include "../block/blk.h" +#include #include "sd.h" #include "scsi.h" #include "hosts.h" @@ -71,10 +71,11 @@ S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -#define AIC7XXX_C_VERSION "$Revision: 2.0 $" +#define AIC7XXX_C_VERSION "$Revision: 2.5 $" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) ((a < b) ? a : b) +#define ALL_TARGETS -1 #ifndef TRUE # define TRUE 1 #endif @@ -84,7 +85,8 @@ /* * Defines for PCI bus support, testing twin bus support, DMAing of - * SCBs, and tagged queueing. + * SCBs, tagged queueing, commands (SCBs) per lun, and SCSI bus reset + * delay time. * * o PCI bus support - this has been implemented and working since * the December 1, 1994 release of this driver. If you don't have @@ -95,7 +97,9 @@ * * o Twin bus support - this has been tested and does work. * - * o DMAing of SCBs - thanks to Kai Makisara, this now works + * o DMAing of SCBs - thanks to Kai Makisara, this now works. + * This define is now taken out and DMAing of SCBs is always + * performed (8/12/95 - DE). * * o Tagged queueing - this driver is capable of tagged queueing * but I am unsure as to how well the higher level driver implements @@ -109,29 +113,55 @@ * PCI code and interrupt handling needs to be modified to * support this. * + * o Commands per lun - If tagged queueing is enabled, then you + * may want to try increasing AIC7XXX_CMDS_PER_LUN to more + * than 2. By default, we limit the SCBs per lun to 2 with + * or without tagged queueing enabled. If tagged queueing is + * disabled, the sequencer will keep the 2nd SCB in the input + * queue until the first one completes - so it is OK to to have + * more than 1 SCB queued. If tagged queueing is enabled, then + * the sequencer will attempt to send the 2nd SCB to the device + * while the first SCB is executing and the device is disconnected. + * For adapters limited to 4 SCBs, you may want to actually + * decrease the commands per lun to 1, if you often have more + * than 2 devices active at the same time. This will allocate + * 1 SCB for each device and ensure that there will always be + * a free SCB for up to 4 devices active at the same time. + * * Daniel M. Eischen, deischen@iworks.InterWorks.org, 03/11/95 */ /* Uncomment this for testing twin bus support. */ #define AIC7XXX_TWIN_SUPPORT -/* Uncomment this for DMAing of SCBs. */ -#define AIC7XXX_USE_DMA - /* Uncomment this for tagged queueing. */ /* #define AIC7XXX_TAGGED_QUEUEING */ /* Uncomment this for allowing sharing of IRQs. */ #define AIC7XXX_SHARE_IRQS +/* + * You can try raising me if tagged queueing is enabled, or lowering + * me if you only have 4 SCBs. + */ +#define AIC7XXX_CMDS_PER_LUN 2 + /* Set this to the delay in seconds after SCSI bus reset. */ #define AIC7XXX_RESET_DELAY 15 /* - * Uncomment this to always use scatter/gather lists. - * *NOTE: The sequencer must be changed also! + * Uncomment the following define for collection of SCSI transfer statistics + * for the /proc filesystem. + * + * NOTE: This does affect performance since it has to maintain statistics. + */ +/* #define AIC7XXX_PROC_STATS */ + +/* + * Define this to use polling rather than using kernel support for waking + * up a waiting process. */ -#define AIC7XXX_USE_SG +#undef AIC7XXX_POLL /* * Controller type and options @@ -157,6 +187,12 @@ AIC_DISABLED } aha_status_type; +typedef enum { + LIST_HEAD, + LIST_SECOND, + LIST_TAIL +} insert_type; + /* * There should be a specific return value for this in scsi.h, but * it seems that most drivers ignore it. @@ -256,6 +292,9 @@ * SCSI Rate */ #define SCSIRATE(x) ((x) + 0xC04ul) +#define WIDEXFER 0x80 /* Wide transfer control */ +#define SXFR 0x70 /* Sync transfer rate */ +#define SOFS 0x0F /* Sync offset */ /* * SCSI ID (p. 3-18). @@ -267,6 +306,15 @@ #define OID 0x0F /* Our ID mask */ /* + * SCSI Transfer Count (pp. 3-19,20) + * These registers count down the number of bytes transfered + * across the SCSI bus. The counter is decremented only once + * the data has been safely transfered. SDONE in SSTAT0 is + * set when STCNT goes to 0 + */ +#define STCNT(x) ((x) + 0xC08ul) + +/* * SCSI Status 0 (p. 3-21) * Contains one set of SCSI Interrupt codes * These are most likely of interest to the sequencer @@ -325,6 +373,16 @@ #define ENREQINIT 0x01 /* + * SCSI/Host Address (p. 3-30) + * These registers hold the host address for the byte about to be + * transfered on the SCSI bus. They are counted up in the same + * manner as STCNT is counted down. SHADDR should always be used + * to determine the address of the last byte transfered since HADDR + * can be squewed by write ahead. + */ +#define SHADDR(x) ((x) + 0xC14ul) + +/* * Selection/Reselection ID (p. 3-31) * Upper four bits are the device id. The ONEBIT is set when the re/selecting * device did not set its own ID. @@ -404,6 +462,7 @@ #define SEQADDR1(x) ((x) + 0xC63ul) #define ACCUM(x) ((x) + 0xC64ul) /* accumulator */ +#define SINDEX(x) ((x) + 0xC65ul) /* * Board Control (p. 3-43) @@ -448,6 +507,13 @@ #define UNPAUSE_294X IRQMS | INTEN /* + * Host Address (p. 3-48) + * This register contains the address of the byte about + * to be transfered across the host bus. + */ +#define HADDR(x) ((x) + 0xC88ul) + +/* * SCB Pointer (p. 3-49) * Gate one of the four SCBs into the SCBARRAY window. */ @@ -469,7 +535,8 @@ #define BAD_STATUS 0x70 #define RESIDUAL 0x80 #define ABORT_TAG 0x90 -#define AWAITING_MSG 0xa0 +#define AWAITING_MSG 0xA0 +#define IMMEDDONE 0xB0 #define BRKADRINT 0x08 #define SCSIINT 0x04 #define CMDCMPLT 0x02 @@ -562,7 +629,7 @@ /* * Bit vector of targets that have disconnection disabled. */ -#define HA_DISC_DSB ((x) + 0xc32ul) +#define HA_DISC_DSB(x) ((x) + 0xC32ul) /* * Length of pending message @@ -586,6 +653,8 @@ #define SEND_WDTR 0x80 #define SEND_REJ 0x40 +#define SG_COUNT(x) ((x) + 0xC4Dul) +#define SG_NEXT(x) ((x) + 0xC4Eul) #define HA_SIGSTATE(x) ((x) + 0xC4Bul) /* value in SCSISIGO */ #define HA_SCBCOUNT(x) ((x) + 0xC52ul) /* number of hardware SCBs */ @@ -607,8 +676,12 @@ #define HA_INTDEF(x) ((x) + 0xC5Cul) /* interrupt def'n register */ #define HA_HOSTCONF(x) ((x) + 0xC5Dul) /* host config def'n register */ +#define HA_274_BIOSCTRL(x) ((x) + 0xC5Ful) /* BIOS enabled for 274x */ +#define BIOSMODE 0x30 +#define BIOSDISABLED 0x30 + #define MSG_ABORT 0x06 -#define MSG_BUS_DEVICE_RESET 0x0c +#define MSG_BUS_DEVICE_RESET 0x0C #define BUS_8_BIT 0x00 #define BUS_16_BIT 0x01 #define BUS_32_BIT 0x02 @@ -734,8 +807,8 @@ * kernel structure hasn't changed. */ #define SG_STRUCT_CHECK(sg) \ - ((char *)&(sg).address - (char *)&(sg) != 0 || \ - (char *)&(sg).length - (char *)&(sg) != 8 || \ + ((char *) &(sg).address - (char *) &(sg) != 0 || \ + (char *) &(sg).length - (char *) &(sg) != 8 || \ sizeof((sg).address) != 4 || \ sizeof((sg).length) != 4 || \ sizeof(sg) != 12) @@ -786,36 +859,24 @@ /* * The driver keeps up to four scb structures per card in memory. Only the * first 26 bytes of the structure are valid for the hardware, the rest used - * for driver level bookeeping. The driver is further optimized - * so that we only have to download the first 19 bytes since as long - * as we always use S/G, the last fields should be zero anyway. + * for driver level bookeeping. */ -#ifdef AIC7XXX_USE_SG -#define SCB_DOWNLOAD_SIZE 19 /* amount to actually download */ -#else -#define SCB_DOWNLOAD_SIZE 26 -#endif - -#define SCB_UPLOAD_SIZE 19 /* amount to actually upload */ +#define SCB_DOWNLOAD_SIZE 26 /* amount to actually download */ +#define SCB_UPLOAD_SIZE 26 /* amount to actually upload */ struct aic7xxx_scb { /* ------------ Begin hardware supported fields ---------------- */ /*1 */ unsigned char control; -#define SCB_NEEDWDTR 0x80 /* Initiate Wide Negotiation */ -#define SCB_NEEDSDTR 0x40 /* Initiate Sync Negotiation */ -#define SCB_NEEDDMA 0x08 /* SCB needs to be DMA'd from - * from host memory - */ -#define SCB_REJ_MDP 0x80 /* Reject MDP message */ -#define SCB_DISEN 0x40 /* SCB Disconnect enable */ -#define SCB_TE 0x20 /* Tag enable */ -/* RESERVED 0x10 */ -#define SCB_WAITING 0x08 /* Waiting */ -#define SCB_DIS 0x04 /* Disconnected */ -#define SCB_TAG_TYPE 0x03 -#define SIMPLE_QUEUE 0x00 /* Simple Queue */ -#define HEAD_QUEUE 0x01 /* Head of Queue */ -#define ORD_QUEUE 0x02 /* Ordered Queue */ +#define SCB_NEEDWDTR 0x80 /* Initiate Wide Negotiation */ +#define SCB_DISCENB 0x40 /* Disconnection Enable */ +#define SCB_TE 0x20 /* Tag enable */ +#define SCB_NEEDSDTR 0x10 /* Initiate Sync Negotiation */ +#define SCB_NEEDDMA 0x08 /* Refresh SCB from host ram */ +#define SCB_DIS 0x04 +#define SCB_TAG_TYPE 0x03 +#define SIMPLE_QUEUE 0x00 +#define HEAD_QUEUE 0x01 +#define OR_QUEUE 0x02 /* ILLEGAL 0x03 */ /*2 */ unsigned char target_channel_lun; /* 4/1/3 bits */ /*3 */ unsigned char SG_segment_count; @@ -830,7 +891,7 @@ /*26*/ unsigned char data_count[3]; /*30*/ unsigned char host_scb[4] __attribute__ ((packed)); /*31*/ u_char next_waiting; /* Used to thread SCBs awaiting selection. */ -#define SCB_LIST_NULL 0x10 /* SCB list equivelent to NULL */ +#define SCB_LIST_NULL 0xFF /* SCB list equivelent to NULL */ #if 0 /* * No real point in transferring this to the @@ -850,13 +911,22 @@ #define SCB_IMMED 0x08 #define SCB_SENSE 0x10 unsigned int position; /* Position in scb array */ -#ifdef AIC7XXX_USE_SG struct scatterlist sg; struct scatterlist sense_sg; -#endif unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */ +#define TIMER_ENABLED 0x01 +#define TIMER_EXPIRED 0x02 +#define TIMED_CMD_DONE 0x04 + volatile unsigned char timer_status; +#ifndef AIC7XXX_POLL + struct wait_queue *waiting; /* wait queue for device reset command */ + struct wait_queue waitq; /* waiting points to this */ + struct timer_list timer; /* timeout for device reset command */ +#endif }; +typedef void (*timeout_fn)(unsigned long); + static struct { unsigned char errno; const char *errmesg; @@ -898,11 +968,34 @@ volatile unsigned short needwdtr_copy; /* default config */ volatile unsigned short needwdtr; volatile unsigned short wdtr_pending; + volatile unsigned short discenable; /* Targets allowed to disconnect */ struct seeprom_config seeprom; int have_seeprom; struct Scsi_Host *next; /* allow for multiple IRQs */ struct aic7xxx_scb scb_array[AIC7XXX_MAXSCB]; /* active commands */ struct aic7xxx_scb *free_scb; /* list of free SCBs */ +#ifdef AIC7XXX_PROC_STATS + /* + * Statistics Kept: + * + * Total Xfers (count for each command that has a data xfer), + * broken down further by reads && writes. + * + * Binned sizes, writes && reads: + * < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K + * + * Total amounts read/written above 512 bytes (amts under ignored) + */ + struct aic7xxx_xferstats { + long xfers; /* total xfer count */ + long w_total; /* total writes */ + long w_total512; /* 512 byte blocks written */ + long w_bins[10]; /* binned write */ + long r_total; /* total reads */ + long r_total512; /* 512 byte blocks read */ + long r_bins[10]; /* binned reads */ + } stats[2][16][8]; /* channel, target, lun */ +#endif /* AIC7XXX_PROC_STATS */ }; struct aic7xxx_host_config { @@ -982,11 +1075,11 @@ */ if ((p->type == AIC_274x) || (p->type == AIC_284x)) { - dfthresh = host_conf >> 6; + dfthresh = (host_conf >> 6); } else { - dfthresh = scsi_conf >> 6; + dfthresh = (scsi_conf >> 6); } brelease = p->busrtime; @@ -1037,7 +1130,10 @@ (scsi_conf & 0x40) ? "en" : "dis"); if (((p->type == AIC_274x) || (p->type == AIC_284x)) && p->parity == AIC_UNKNOWN) - { /* Set the parity for 7770 based cards. */ + { + /* + * Set the parity for 7770 based cards. + */ p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED; } if (p->parity != AIC_UNKNOWN) @@ -1061,9 +1157,35 @@ (p->high_term == AIC_ENABLED) ? "en" : "dis"); } } + +static void +debug_scb(struct aic7xxx_scb *scb) +{ + printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n", + scb->control, scb->target_channel_lun, scb->SG_segment_count, + (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) | + (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0], + (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) | + (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0], + scb->SCSI_cmd_length); + printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n", + (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status, + scb->residual_SG_segment_count, (scb->residual_data_count[2] << 16) | + (scb->residual_data_count[1] << 8) | scb->residual_data_count[0]); + printk("data ptr 0x%x, data count %d, host scb 0x%x, next waiting %d\n", + (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) | + (scb->data_pointer[1] << 8) | scb->data_pointer[0], + (scb->data_count[2] << 16) | (scb->data_count[1] << 8) | scb->data_count[0], + (unsigned int) scb->host_scb, scb->next_waiting); + printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n", + (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state, + scb->position); +} + #else # define debug(fmt, args...) # define debug_config(x) +# define debug_scb(x) #endif AIC7XXX_DEBUG /* @@ -1189,6 +1311,109 @@ } } +#ifdef AIC7XXX_POLL +/*+F************************************************************************* + * Function: + * aic7xxx_poll_scb + * + * Description: + * Function to poll for command completion when in aborting an SCB. + *-F*************************************************************************/ +static void +aic7xxx_poll_scb(struct aic7xxx_host *p, + struct aic7xxx_scb *scb, + unsigned long timeout_ticks) +{ + unsigned long timer_expiration = jiffies + timeout_ticks; + + while ((jiffies < timer_expiration) && !(scb->timer_status & TIMED_CMD_DONE)) + { + udelay(1000); /* delay for 1 msec. */ + } +} + +#else +/*+F************************************************************************* + * Function: + * aic7xxx_scb_timeout + * + * Description: + * Called when a SCB reset command times out. The input is actually + * a pointer to the SCB. + *-F*************************************************************************/ +static void +aic7xxx_scb_timeout(unsigned long data) +{ + struct aic7xxx_scb *scb = (struct aic7xxx_scb *) data; + + scb->timer_status |= TIMER_EXPIRED; + wake_up(&(scb->waiting)); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_scb_untimeout + * + * Description: + * This function clears the timeout and wakes up a waiting SCB. + *-F*************************************************************************/ +static void +aic7xxx_scb_untimeout(struct aic7xxx_scb *scb) +{ + if (scb->timer_status & TIMER_ENABLED) + { + scb->timer_status = TIMED_CMD_DONE; + wake_up(&(scb->waiting)); + } +} +#endif + +/*+F************************************************************************* + * Function: + * aic7xxx_scb_tsleep + * + * Description: + * Emulates a BSD tsleep where a process can sleep for a specified + * amount of time, but may be awakened before that. Linux provides + * a sleep_on, wake_up, add_timer, and del_timer which can be used to + * emulate tsleep, but there's not enough information available on + * how to use them. For now, we'll just poll for SCB completion. + * + * The parameter ticks is the number of clock ticks + * to wait before a timeout. A 0 is returned if the scb does not + * timeout, 1 is returned for a timeout. + *-F*************************************************************************/ +static int +aic7xxx_scb_tsleep(struct aic7xxx_host *p, + struct aic7xxx_scb *scb, + unsigned long ticks) +{ + scb->timer_status = TIMER_ENABLED; +#ifdef AIC7XXX_POLL + UNPAUSE_SEQUENCER(p); + aic7xxx_poll_scb(p, scb, ticks); +#else + scb->waiting = &(scb->waitq); + scb->timer.expires = jiffies + ticks; + scb->timer.data = (unsigned long) scb; + scb->timer.function = (timeout_fn) aic7xxx_scb_timeout; + add_timer(&scb->timer); + UNPAUSE_SEQUENCER(p); + sleep_on(&(scb->waiting)); + del_timer(&scb->timer); +#endif + if (!(scb->timer_status & TIMED_CMD_DONE)) + { + scb->timer_status = 0x0; + return (1); + } + else + { + scb->timer_status = 0x0; + return (0); + } +} + /*+F************************************************************************* * Function: * rcs_version @@ -1265,6 +1490,73 @@ /*+F************************************************************************* * Function: + * aic7xxx_length + * + * Description: + * How much data should be transferred for this SCSI command? Stop + * at segment sg_last if it's a scatter-gather command so we can + * compute underflow easily. + *-F*************************************************************************/ +static unsigned +aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) +{ + int i, segments; + unsigned length; + struct scatterlist *sg; + + segments = cmd->use_sg - sg_last; + sg = (struct scatterlist *) cmd->buffer; + + if (cmd->use_sg) + { + for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++) + { + length += sg[i].length; + } + } + else + { + length = cmd->request_bufflen; + } + + return (length); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_scsirate + * + * Description: + * Look up the valid period to SCSIRATE conversion in our table + *-F*************************************************************************/ +static void +aic7xxx_scsirate(unsigned char *scsirate, unsigned char period, + unsigned char offset, int target, char channel) +{ + int i; + + for (i = 0; i < num_aic7xxx_syncrates; i++) + { + if ((aic7xxx_syncrates[i].period - period) >= 0) + { + *scsirate = (aic7xxx_syncrates[i].rate << 4) | (offset & 0x0F); + printk("aic7xxx: target %d, channel %c, now synchronous at %sMb/s, " + "offset = 0x%x\n", + target, channel, aic7xxx_syncrates[i].english, offset); + return; + } + } + + /* + * Default to asyncronous transfer + */ + *scsirate = 0; + printk("aic7xxx: target %d, channel %c, using asynchronous transfers\n", + target, channel); +} + +/*+F************************************************************************* + * Function: * aic7xxx_putscb * * Description: @@ -1273,42 +1565,23 @@ static void aic7xxx_putscb(int base, struct aic7xxx_scb *scb) { -#ifdef AIC7XXX_USE_DMA /* * All we need to do, is to output the position * of the SCB in the SCBARRAY to the QINFIFO * of the host adapter. */ outb(scb->position, QINFIFO(base)); -#else - /* - * By turning on the SCB auto increment, any reference - * to the SCB I/O space postincrements the SCB address - * we're looking at. So turn this on and dump the relevant - * portion of the SCB to the card. - */ - outb(SCBAUTO, SCBCNT(base)); - - asm volatile("cld\n\t" - "rep\n\t" - "outsb" - : /* no output */ - :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base)) - :"si", "cx", "dx"); - - outb(0, SCBCNT(base)); -#endif } /*+F************************************************************************* * Function: - * aic7xxx_putdmascb + * aic7xxx_putscb_dma * * Description: * DMA a SCB to the controller. *-F*************************************************************************/ static void -aic7xxx_putdmascb(int base, struct aic7xxx_scb *scb) +aic7xxx_putscb_dma(int base, struct aic7xxx_scb *scb) { /* * By turning on the SCB auto increment, any reference @@ -1355,67 +1628,467 @@ /*+F************************************************************************* * Function: - * aic7xxx_length + * aic7xxx_match_scb * * Description: - * How much data should be transferred for this SCSI command? Stop - * at segment sg_last if it's a scatter-gather command so we can - * compute underflow easily. + * Checks to see if an scb matches the target/channel as specified. + * If target is ALL_TARGETS (-1), then we're looking for any device + * on the specified channel; this happens when a channel is going + * to be reset and all devices on that channel must be aborted. *-F*************************************************************************/ -static unsigned -aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) +static int +aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel) { - int i, segments; - unsigned length; - struct scatterlist *sg; + int targ = (scb->target_channel_lun >> 4) & 0x0F; + char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; - segments = cmd->use_sg - sg_last; - sg = (struct scatterlist *) cmd->buffer; + if (target == ALL_TARGETS) + { + return (chan == channel); + } + else + { + return ((chan == channel) && (targ == target)); + } +} - if (cmd->use_sg) +/*+F************************************************************************* + * Function: + * aic7xxx_unbusy_target + * + * Description: + * Set the specified target inactive. + *-F*************************************************************************/ +static void +aic7xxx_unbusy_target(unsigned char target, char channel, int base) +{ + unsigned char active; + unsigned long active_port = HA_ACTIVE0(base); + + if ((target > 0x07) || (channel == 'B')) + { + /* + * targets on the Second channel or above id 7 store info in byte two + * of HA_ACTIVE + */ + active_port++; + } + active = inb(active_port); + active &= ~(0x01 << (target & 0x07)); + outb(active_port, active); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_done + * + * Description: + * Calls the higher level scsi done function and frees the scb. + *-F*************************************************************************/ +static void +aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) +{ + long flags; + Scsi_Cmnd *cmd = scb->cmd; + + if (scb->timer_status & TIMER_ENABLED) + { +#ifdef AIC7XXX_POLL + scb->timer_status |= TIMED_CMD_DONE; +#else + aic7xxx_scb_untimeout(scb); +#endif + } + else + { + /* + * This is a critical section, since we don't want the + * queue routine mucking with the host data. + */ + save_flags(flags); + cli(); + + /* + * Process the command after marking the scb as free + * and adding it to the free list. + */ + scb->state = SCB_FREE; + scb->next = p->free_scb; + p->free_scb = &(p->scb_array[scb->position]); + scb->cmd = NULL; + + restore_flags(flags); + + cmd->scsi_done(cmd); + } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_add_waiting_scb + * + * Description: + * Add this SCB to the "waiting for selection" list. + *-F*************************************************************************/ +static void +aic7xxx_add_waiting_scb(u_long base, + struct aic7xxx_scb *scb, + insert_type where) +{ + unsigned char head, tail; + unsigned char curscb; + + curscb = inb(SCBPTR(base)); + head = inb(WAITING_SCBH(base)); + tail = inb(WAITING_SCBT(base)); + if (head == SCB_LIST_NULL) + { + /* + * List was empty + */ + head = scb->position; + tail = SCB_LIST_NULL; + } + else { - for (i = length = 0; i < cmd->use_sg && i < segments; i++) + if (where == LIST_HEAD) { - length += sg[i].length; + outb(scb->position, SCBPTR(base)); + outb(head, SCBARRAY(base) + 30); + head = scb->position; } + else + { + if (tail == SCB_LIST_NULL) + { + /* + * List had one element + */ + tail = scb->position; + outb(head, SCBPTR(base)); + outb(tail, SCBARRAY(base) + 30); + } + else + { + if (where == LIST_SECOND) + { + unsigned char third_scb; + + outb(head, SCBPTR(base)); + third_scb = inb(SCBARRAY(base) + 30); + outb(scb->position, SCBARRAY(base) + 30); + outb(scb->position, SCBPTR(base)); + outb(third_scb, SCBARRAY(base) + 30); + } + else + { + outb(tail, SCBPTR(base)); + tail = scb->position; + outb(tail, SCBARRAY(base) + 30); + } + } + } + } + outb(head, WAITING_SCBH(base)); + outb(tail, WAITING_SCBT(base)); + outb(curscb, SCBPTR(base)); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_abort_waiting_scb + * + * Description: + * Manipulate the waiting for selection list and return the + * scb that follows the one that we remove. + *-F*************************************************************************/ +static unsigned char +aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb, + unsigned char prev, unsigned char timedout_scb) +{ + unsigned char curscb, next; + int target = (scb->target_channel_lun >> 4) & 0x0F; + char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + int base = p->base; + + /* + * Select the SCB we want to abort and + * pull the next pointer out of it. + */ + curscb = inb(SCBPTR(base)); + outb(scb->position, SCBPTR(base)); + next = inb(SCBARRAY(base) + 30); + + /* + * Clear the necessary fields + */ + outb(SCB_NEEDDMA, SCBARRAY(base)); + outb(SCB_LIST_NULL, SCBARRAY(base) + 30); + aic7xxx_unbusy_target(target, channel, base); + + /* + * Update the waiting list + */ + if (prev == SCB_LIST_NULL) + { + /* + * First in the list + */ + outb(next, WAITING_SCBH(base)); } else { - length = cmd->request_bufflen; + /* + * Select the scb that pointed to us and update its next pointer. + */ + outb(prev, SCBPTR(base)); + outb(next, SCBARRAY(base) + 30); + } + /* + * Update the tale pointer + */ + if (inb(WAITING_SCBT(base)) == scb->position) + { + outb(prev, WAITING_SCBT(base)); } - return (length); + /* + * Point us back at the original scb position + * and inform the SCSI system that the command + * has been aborted. + */ + outb(curscb, SCBPTR(base)); + scb->state |= SCB_ABORTED; + scb->cmd->result = (DID_RESET << 16); + aic7xxx_done(p, scb); + + return (next); } /*+F************************************************************************* * Function: - * aic7xxx_scsirate + * aic7xxx_reset_device * * Description: - * Look up the valid period to SCSIRATE conversion in our table + * The device at the given target/channel has been reset. Abort + * all active and queued scbs for that target/channel. + *-F*************************************************************************/ +static int +aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel, + unsigned char timedout_scb) +{ + int base = p->base; + struct aic7xxx_scb *scb; + unsigned char active_scb; + int i = 0; + int found = 0; + + /* + * Restore this when we're done + */ + active_scb = inb(SCBPTR(base)); + + /* + * Search the QINFIFO. + */ + { + int saved_queue[AIC7XXX_MAXSCB]; + int queued = inb(QINCNT(base)); + + for (i = 0; i < (queued - found); i++) + { + saved_queue[i] = inb(QINFIFO(base)); + scb = &(p->scb_array[saved_queue[i]]); + if (aic7xxx_match_scb(scb, target, channel)) + { + /* + * We found an scb that needs to be aborted. + */ + scb->state |= SCB_ABORTED; + scb->cmd->result = (DID_RESET << 16); + aic7xxx_done(p, scb); + outb(scb->position, SCBPTR(base)); + outb(SCB_NEEDDMA, SCBARRAY(base)); + i--; + found++; + } + } + /* + * Now put the saved scbs back. + */ + for (queued = 0; queued < i; queued++) + { + outb(saved_queue[queued], QINFIFO(base)); + } + } + + /* + * Search waiting for selection list. + */ + { + unsigned char next, prev; + + next = inb(WAITING_SCBH(base)); /* Start at head of list. */ + prev = SCB_LIST_NULL; + + while (next != SCB_LIST_NULL) + { + scb = &(p->scb_array[next]); + /* + * Select the SCB. + */ + if (aic7xxx_match_scb(scb, target, channel)) + { + next = aic7xxx_abort_waiting_scb(p, scb, prev, timedout_scb); + found++; + } + else + { + outb(scb->position, SCBPTR(base)); + prev = next; + next = inb(SCBARRAY(base) + 30); + } + } + } + + /* + * Go through the entire SCB array now and look for + * commands for this target that are active. These + * are other (most likely tagged) commands that + * were disconnected when the reset occured. + */ + for(i = 0; i < p->numscb; i++) + { + scb = &(p->scb_array[i]); + if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel)) + { + /* + * Ensure the target is "free" + */ + aic7xxx_unbusy_target(target, channel, base); + outb(scb->position, SCBPTR(base)); + outb(SCB_NEEDDMA, SCBARRAY(base)); + scb->state |= SCB_ABORTED; + scb->cmd->result = (DID_RESET << 16); + aic7xxx_done(p, scb); + found++; + } + } + + outb(active_scb, SCBPTR(base)); + return (found); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_reset_current_bus + * + * Description: + * Reset the current SCSI bus. *-F*************************************************************************/ static void -aic7xxx_scsirate(unsigned char *scsirate, unsigned char period, - unsigned char offset, int target) +aic7xxx_reset_current_bus(int base) { - int i; + outb(SCSIRSTO, SCSISEQ(base)); + udelay(1000); + outb(0, SCSISEQ(base)); +} - for (i = 0; i < num_aic7xxx_syncrates; i++) +/*+F************************************************************************* + * Function: + * aic7xxx_reset_channel + * + * Description: + * Reset the channel. + *-F*************************************************************************/ +static int +aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, + unsigned char timedout_scb) +{ + int base = p->base; + unsigned char sblkctl; + char cur_channel; + unsigned long offset, offset_max; + int found; + + /* + * Clean up all the state information for the + * pending transactions on this bus. + */ + found = aic7xxx_reset_device(p, ALL_TARGETS, channel, timedout_scb); + + if (channel == 'B') { - if ((aic7xxx_syncrates[i].period - period) >= 0) + p->needsdtr |= (p->needsdtr_copy & 0xFF00); + p->sdtr_pending &= 0x00FF; + outb(0, HA_ACTIVE1(base)); + offset = HA_TARG_SCRATCH(base) + 8; + offset_max = HA_TARG_SCRATCH(base) + 16; + } + else + { + if (p->bus_type == AIC_WIDE) { - *scsirate = (aic7xxx_syncrates[i].rate << 4) | (offset & 0x0F); - printk("aic7xxx: target %d now synchronous at %sMb/s, offset = 0x%x\n", - target, aic7xxx_syncrates[i].english, offset); - return; + p->needsdtr = p->needsdtr_copy; + p->needwdtr = p->needwdtr_copy; + p->sdtr_pending = 0; + p->wdtr_pending = 0; + outb(0, HA_ACTIVE0(base)); + outb(0, HA_ACTIVE1(base)); + offset = HA_TARG_SCRATCH(base); + offset_max = HA_TARG_SCRATCH(base) + 16; } + else + { + p->needsdtr |= (p->needsdtr_copy & 0x00FF); + p->sdtr_pending &= 0xFF00; + outb(0, HA_ACTIVE0(base)); + offset = HA_TARG_SCRATCH(base); + offset_max = HA_TARG_SCRATCH(base) + 8; + } + } + while (offset < offset_max) + { + /* + * Revert to async/narrow transfers + * until we renegotiate. + */ + u_char targ_scratch; + targ_scratch = inb(offset); + targ_scratch &= SXFR; + outb(targ_scratch, offset); + offset++; } /* - * Default to asyncronous transfer + * Reset the bus and unpause/restart the controller */ - *scsirate = 0; - printk("aic7xxx: target %d using asynchronous transfers\n", target); + + /* + * Case 1: Command for another bus is active + */ + sblkctl = inb(SBLKCTL(base)); + cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A'; + if (cur_channel != channel) + { + /* + * Stealthily reset the other bus without upsetting the current bus + */ + outb(sblkctl ^ SELBUSB, SBLKCTL(base)); + aic7xxx_reset_current_bus(base); + outb(sblkctl, SBLKCTL(base)); + UNPAUSE_SEQUENCER(p); + } + /* + * Case 2: A command from this bus is active or we're idle + */ + else + { + aic7xxx_reset_current_bus(base); + RESTART_SEQUENCER(p); + } + + return found; } /*+F************************************************************************* @@ -1434,17 +2107,15 @@ int base, intstat; struct aic7xxx_host *p; struct aic7xxx_scb *scb; - unsigned char active, ha_flags, transfer; + unsigned char ha_flags, transfer; unsigned char scsi_id, bus_width; - unsigned char offset, rate, scratch; + unsigned char offset, rate, scratch, scratch_offset; unsigned char max_offset, rej_byte; - unsigned char head, tail; - unsigned short target_mask; - long flags; + unsigned short target_mask, active; + char channel; void *addr; int actual; - int target, tcl; - int scbptr; + int scb_index; Scsi_Cmnd *cmd; p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; @@ -1483,7 +2154,10 @@ } } - p->isr_count++; /* Keep track of interrupts for /proc/scsi */ + /* + * Keep track of interrupts for /proc/scsi + */ + p->isr_count++; if ((p->a_scanned == 0) && (p->isr_count == 1)) { @@ -1519,20 +2193,28 @@ } panic("aic7xxx_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n", - inb(ERROR(base)), - inb(SEQADDR1(base)) << 8 | inb(SEQADDR0(base))); + inb(ERROR(base)), (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base))); } if (intstat & SEQINT) { /* * Although the sequencer is paused immediately on - * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT - * condition will have unpaused the sequencer before - * this point. + * a SEQINT, an interrupt for a SCSIINT condition will + * unpaused the sequencer before this point. */ PAUSE_SEQUENCER(p); + scsi_id = (inb(SCSIID(base)) >> 4) & 0x0F; + scratch_offset = scsi_id; + channel = 'A'; + if (inb(SBLKCTL(base)) & SELBUSB) + { + channel = 'B'; + scratch_offset += 8; + } + target_mask = (0x01 << scratch_offset); + switch (intstat & SEQINT_MASK) { case BAD_PHASE: @@ -1541,9 +2223,6 @@ case SEND_REJECT: rej_byte = inb(HA_REJBYTE(base)); - scsi_id = inb(SCSIID(base)) >> 0x04; - scbptr = inb(SCBPTR(base)); - scb = &(p->scb_array[scbptr]); if (rej_byte != 0x20) { debug("aic7xxx_isr warning: issuing message reject, 1st byte 0x%x\n", @@ -1551,53 +2230,28 @@ } else { + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); printk("aic7xxx_isr warning: Tagged message rejected for target %d," - " channel %c.\n", - scsi_id, (inb(SBLKCTL(base)) & SELBUSB ? 'B': 'A')); + " channel %c.\n", scsi_id, channel); scb->cmd->device->tagged_supported = 0; scb->cmd->device->tagged_queue = 0; } break; case NO_IDENT: - panic("aic7xxx_isr: reconnecting target %d at seqaddr 0x%x " - "didn't issue IDENTIFY message\n", - (inb(SELID(base)) >> 4) & 0x0F, - (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base))); + panic("aic7xxx_isr: Target %d, channel %c, did not send an IDENTIFY " + "message. SAVED_TCL = 0x%x\n", + scsi_id, channel, inb(SAVED_TCL(base))); break; case NO_MATCH: - tcl = inb(SCBARRAY(base) + 1); - target = (tcl >> 4) & 0x0F; - /* Purposefully mask off the top bit of targets 8-15. */ - target_mask = 0x01 << (target & 0x07); - - debug("aic7xxx_isr: sequencer couldn't find match " - "for reconnecting target %d, channel %d, lun %d - " - "issuing ABORT\n", target, (tcl & 0x08) >> 3, tcl & 0x07); - if (tcl & 0x88) - { - /* Second channel stores its info in byte - * two of HA_ACTIVE - */ - active = inb(HA_ACTIVE1(base)); - active = active & ~(target_mask); - outb(active, HA_ACTIVE1(base)); - } - else - { - active = inb(HA_ACTIVE0(base)); - active = active & ~(target_mask); - outb(active, HA_ACTIVE0(base)); - } -#ifdef AIC7XXX_USE_DMA - outb(SCB_NEEDDMA, SCBARRAY(base)); -#endif + printk("aic7xxx_isr: No active SCB for reconnecting target %d, " + "channel %c - issuing ABORT\n", scsi_id, channel); + printk("SAVED_TCL = 0x%x\n", inb(SAVED_TCL(base))); + aic7xxx_unbusy_target(scsi_id, channel, base); + outb(SCB_NEEDDMA, SCBARRAY(base)); - /* - * Check out why this use to be outb(0x80, CLRINT(base)) - * clear the timeout - */ outb(CLRSELTIMEO, CLRSINT1(base)); RESTART_SEQUENCER(p); break; @@ -1611,33 +2265,27 @@ */ transfer = (inb(HA_ARG_1(base)) << 2); offset = inb(ACCUM(base)); - scsi_id = inb(SCSIID(base)) >> 0x04; - if (inb(SBLKCTL(base)) & 0x08) - { - scsi_id = scsi_id + 8; /* B channel */ - } - target_mask = (0x01 << scsi_id); - scratch = inb(HA_TARG_SCRATCH(base) + scsi_id); + scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset); /* * The maximum offset for a wide device is 0x08; for a - * 8-bit bus device the maximum offset is 0x0f. + * 8-bit bus device the maximum offset is 0x0F. */ - if (scratch & 0x80) + if (scratch & WIDEXFER) { max_offset = 0x08; } else { - max_offset = 0x0f; + max_offset = 0x0F; } - aic7xxx_scsirate(&rate, transfer, MIN(offset, max_offset), scsi_id); + aic7xxx_scsirate(&rate, transfer, MIN(offset, max_offset), scsi_id, channel); /* * Preserve the wide transfer flag. */ - rate = rate | (scratch & 0x80); - outb(rate, HA_TARG_SCRATCH(base) + scsi_id); - outb(rate, SCSIRATE(base)); - if ((rate & 0xf) == 0) + scratch = rate | (scratch & WIDEXFER); + outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset); + outb(scratch, SCSIRATE(base)); + if ((scratch & 0x0F) == 0) { /* * The requested rate was so low that asynchronous transfers * are faster (not to mention the controller won't support @@ -1670,23 +2318,23 @@ /* * Clear the flags. */ - p->needsdtr = p->needsdtr & ~target_mask; - p->sdtr_pending = p->sdtr_pending & ~target_mask; + p->needsdtr &= ~target_mask; + p->sdtr_pending &= ~target_mask; +#if 0 + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); + debug_scb(scb); +#endif + break; case MSG_WDTR: { bus_width = inb(ACCUM(base)); - scsi_id = inb(SCSIID(base)) >> 0x04; - if (inb(SBLKCTL(base)) & 0x08) - { - scsi_id = scsi_id + 8; /* B channel */ - } - printk("aic7xxx_isr: Received MSG_WDTR, scsi_id = %d, " - "needwdtr = 0x%x\n", scsi_id, p->needwdtr); - scratch = inb(HA_TARG_SCRATCH(base) + scsi_id); + printk("aic7xxx_isr: Received MSG_WDTR, scsi_id %d, channel %c " + "needwdtr = 0x%x\n", scsi_id, channel, p->needwdtr); + scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset); - target_mask = (0x01 << scsi_id); if (p->wdtr_pending & target_mask) { /* @@ -1696,13 +2344,13 @@ switch (bus_width) { case BUS_8_BIT: - scratch = scratch & 0x7F; + scratch &= 0x7F; break; case BUS_16_BIT: - printk("aic7xxx_isr: target %d using 16 bit transfers\n", - scsi_id); - scratch = scratch | 0x80; + printk("aic7xxx_isr: target %d, channel %c, using 16 bit transfers\n", + scsi_id, channel); + scratch |= 0x80; break; } } @@ -1715,25 +2363,27 @@ switch (bus_width) { case BUS_8_BIT: - scratch = scratch & 0x7F; + scratch &= 0x7F; break; case BUS_32_BIT: - /* Negotiate 16 bits. */ + /* + * Negotiate 16 bits. + */ bus_width = BUS_16_BIT; - /* Yes, we mean to fall thru here */ + /* Yes, we mean to fall thru here. */ case BUS_16_BIT: - printk("aic7xxx_isr: target %d using 16 bit transfers\n", - scsi_id); - scratch = scratch | 0x80; + printk("aic7xxx_isr: target %d, channel %c, using 16 bit transfers\n", + scsi_id, channel); + scratch |= 0x80; break; } outb(bus_width | SEND_WDTR, HA_RETURN_1(base)); } - p->needwdtr = p->needwdtr & ~target_mask; - p->wdtr_pending = p->wdtr_pending & ~target_mask; - outb(scratch, HA_TARG_SCRATCH(base) + scsi_id); + p->needwdtr &= ~target_mask; + p->wdtr_pending &= ~target_mask; + outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset); outb(scratch, SCSIRATE(base)); break; } @@ -1747,64 +2397,52 @@ * the target is refusing negotiation. */ - unsigned char targ_scratch, scsi_id; - unsigned short mask; + scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset); - scsi_id = inb(SCSIID(base)) >> 0x04; - if (inb(SBLKCTL(base)) & 0x08) - { - scsi_id = scsi_id + 8; - } - - mask = (0x01 << scsi_id); - - targ_scratch = inb(HA_TARG_SCRATCH(base) + scsi_id); - - if (p->wdtr_pending & mask) + if (p->wdtr_pending & target_mask) { /* * note 8bit xfers and clear flag */ - targ_scratch = targ_scratch & 0x7F; - p->needwdtr = p->needwdtr & ~mask; - p->wdtr_pending = p->wdtr_pending & ~mask; - outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id); - printk("aic7xxx: target %d refusing WIDE negotiation. Using " - "8 bit transfers\n", scsi_id); + scratch &= 0x7F; + p->needwdtr &= ~target_mask; + p->wdtr_pending &= ~target_mask; + outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset); + printk("aic7xxx: target %d, channel %c, refusing WIDE negotiation. " + "Using 8 bit transfers\n", scsi_id, channel); } else { - if (p->sdtr_pending & mask) + if (p->sdtr_pending & target_mask) { /* * note asynch xfers and clear flag */ - targ_scratch = targ_scratch & 0xF0; - p->needsdtr = p->needsdtr & ~mask; - p->sdtr_pending = p->sdtr_pending & ~mask; - outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id); - printk("aic7xxx: target %d refusing syncronous negotiation. Using " - "asyncronous transfers\n", scsi_id); + scratch &= 0xF0; + p->needsdtr &= ~target_mask; + p->sdtr_pending &= ~target_mask; + outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset); + printk("aic7xxx: target %d, channel %c, refusing syncronous negotiation. " + "Using asyncronous transfers\n", scsi_id, channel); } /* * Otherwise, we ignore it. */ } - outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id); - outb(targ_scratch, SCSIRATE(base)); + outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset); + outb(scratch, SCSIRATE(base)); break; } case BAD_STATUS: - scsi_id = inb(SCSIID(base)) >> 0x04; - scbptr = inb(SCBPTR(base)); - scb = &(p->scb_array[scbptr]); + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); outb(0, HA_RETURN_1(base)); /* CHECK_CONDITION may change this */ if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL)) { printk("aic7xxx_isr: referenced scb not valid " "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n", - intstat, scbptr, scb->state, (unsigned int) scb->cmd); + intstat, scb_index, scb->state, (unsigned int) scb->cmd); } else { @@ -1812,62 +2450,51 @@ aic7xxx_getscb(base, scb); aic7xxx_status(cmd) = scb->target_status; - cmd->result = cmd->result | scb->target_status; + cmd->result |= scb->target_status; - /* - * This test is just here for debugging purposes. - * It will go away when the timeout problem is resolved. - */ switch (status_byte(scb->target_status)) { case GOOD: + printk("aic7xxx_isr: Interrupted for status of 0???\n"); break; case CHECK_CONDITION: if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE)) { + unsigned char tcl; + unsigned char control; void *req_buf; -#ifndef AIC7XXX_USE_SG - unsigned int req_buflen; -#endif - - /* Update the timeout for the SCSI command. */ -/* update_timeout(cmd, SENSE_TIMEOUT); */ - /* Send a sense command to the requesting target. */ - cmd->flags = cmd->flags | WAS_SENSE; + tcl = scb->target_channel_lun; + /* + * Send a sense command to the requesting target. + */ + cmd->flags |= WAS_SENSE; memcpy((void *) scb->sense_cmd, (void *) generic_sense, sizeof(generic_sense)); - scb->sense_cmd[1] = cmd->lun << 5; + scb->sense_cmd[1] = (cmd->lun << 5); scb->sense_cmd[4] = sizeof(cmd->sense_buffer); -#ifdef AIC7XXX_USE_SG scb->sense_sg.address = (char *) &cmd->sense_buffer; scb->sense_sg.length = sizeof(cmd->sense_buffer); req_buf = &scb->sense_sg; -#else - req_buf = &cmd->sense_buffer; - req_buflen = sizeof(cmd->sense_buffer); -#endif cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); + control = scb->control; memset(scb, 0, SCB_DOWNLOAD_SIZE); - scb->target_channel_lun = ((cmd->target << 4) & 0xF0) | - ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07); + scb->control = control & SCB_DISCENB; + scb->target_channel_lun = tcl; addr = scb->sense_cmd; scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); -#ifdef AIC7XXX_USE_SG scb->SG_segment_count = 1; memcpy(scb->SG_list_pointer, &req_buf, sizeof(scb->SG_list_pointer)); -#else - scb->SG_segment_count = 0; - memcpy(scb->data_pointer, &req_buf, - sizeof(scb->data_pointer)); - memcpy(scb->data_count, &req_buflen, 3); -#endif + scb->data_count[0] = scb->sense_sg.length & 0xFF; + scb->data_count[1] = (scb->sense_sg.length >> 8) & 0xFF; + scb->data_count[2] = (scb->sense_sg.length >> 16) & 0xFF; + memcpy(scb->data_pointer, &(scb->sense_sg.address), 4); outb(SCBAUTO, SCBCNT(base)); asm volatile("cld\n\t" @@ -1878,34 +2505,16 @@ :"si", "cx", "dx"); outb(0, SCBCNT(base)); outb(SCB_LIST_NULL, (SCBARRAY(base) + 30)); + /* + * Ensure that the target is "BUSY" so we don't get overlapping + * commands if we happen to be doing tagged I/O. + */ + active = inb(HA_ACTIVE0(base)) | (inb(HA_ACTIVE1(base)) << 8); + active |= target_mask; + outb(active & 0xFF, HA_ACTIVE0(base)); + outb((active >> 8) & 0xFF, HA_ACTIVE1(base)); - /* - * Add this SCB to the "waiting for selection" list. - */ - head = inb(WAITING_SCBH(base)); - tail = inb(WAITING_SCBT(base)); - if (head & SCB_LIST_NULL) - { /* list is empty */ - head = scb->position; - tail = SCB_LIST_NULL; - } - else - { - if (tail & SCB_LIST_NULL) - { /* list has one element */ - tail = scb->position; - outb(head, SCBPTR(base)); - outb(tail, (SCBARRAY(base) + 30)); - } - else - { /* list has more than one element */ - outb(tail, SCBPTR(base)); - tail = scb->position; - outb(tail, (SCBARRAY(base) + 30)); - } - } - outb(head, WAITING_SCBH(base)); - outb(tail, WAITING_SCBT(base)); + aic7xxx_add_waiting_scb(base, scb, LIST_HEAD); outb(SEND_SENSE, HA_RETURN_1(base)); } /* first time sense, no errors */ else @@ -1915,7 +2524,7 @@ * a normal command complete, and have the scsi driver handle * this condition. */ - cmd->flags = cmd->flags | ASKED_FOR_SENSE; + cmd->flags |= ASKED_FOR_SENSE; } break; @@ -1948,13 +2557,13 @@ break; case RESIDUAL: - scbptr = inb(SCBPTR(base)); - scb = &(p->scb_array[scbptr]); + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL)) { printk("aic7xxx_isr: referenced scb not valid " "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n", - intstat, scbptr, scb->state, (unsigned int) scb->cmd); + intstat, scb_index, scb->state, (unsigned int) scb->cmd); } else { @@ -1980,9 +2589,8 @@ if (actual < cmd->underflow) { printk("aic7xxx: target %d underflow - " - "wanted (at least) %u, got %u\n", - cmd->target, cmd->underflow, actual); - + "wanted (at least) %u, got %u, count=%d\n", + cmd->target, cmd->underflow, actual, inb(SCBARRAY(base + 18))); aic7xxx_error(cmd) = DID_RETRY_COMMAND; aic7xxx_status(cmd) = scb->target_status; } @@ -1991,13 +2599,13 @@ break; case ABORT_TAG: - scbptr = inb(SCBPTR(base)); - scb = &(p->scb_array[scbptr]); + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL)) { printk("aic7xxx_isr: referenced scb not valid " "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n", - intstat, scbptr, scb->state, (unsigned int) scb->cmd); + intstat, scb_index, scb->state, (unsigned int) scb->cmd); } else { @@ -2008,38 +2616,21 @@ */ printk("aic7xxx_isr: invalid tag recieved on channel %c " "target %d, lun %d -- sending ABORT_TAG\n", - (cmd->channel & 0x01) ? 'B':'A', - cmd->target, cmd->lun & 0x07); - /* - * This is a critical section, since we don't want the - * queue routine mucking with the host data. - */ - save_flags(flags); - cli(); - - /* - * Process the command after marking the scb as free - * and adding it to the free list. - */ - scb->state = SCB_FREE; - scb->cmd = NULL; - scb->next = p->free_scb; /* preserve next pointer */ - p->free_scb = scb; /* add at head of list */ + channel, scsi_id, cmd->lun & 0x07); - restore_flags(flags); cmd->result = (DID_RETRY_COMMAND << 16); - cmd->scsi_done(cmd); + aic7xxx_done(p, scb); } break; case AWAITING_MSG: - scbptr = inb(SCBPTR(base)); - scb = &(p->scb_array[scbptr]); + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL)) { printk("aic7xxx_isr: referenced scb not valid " "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n", - intstat, scbptr, scb->state, (unsigned int) scb->cmd); + intstat, scb_index, scb->state, (unsigned int) scb->cmd); } else { @@ -2061,6 +2652,32 @@ } break; + case IMMEDDONE: + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); + if (scb->state & SCB_DEVICE_RESET) + { + int found; + + /* + * Go back to async/narrow transfers and renogiate. + */ + aic7xxx_unbusy_target(scsi_id, channel, base); + p->needsdtr |= (p->needsdtr_copy & target_mask); + p->needwdtr |= (p->needwdtr_copy & target_mask); + p->sdtr_pending &= ~target_mask; + p->wdtr_pending &= ~target_mask; + scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset); + scratch &= SXFR; + outb(scratch, HA_TARG_SCRATCH(base)); + found = aic7xxx_reset_device(p, (int) scsi_id, channel, SCB_LIST_NULL); + } + else + { + panic("aic7xxx_isr: Immediate complete for unknown operation.\n"); + } + break; + default: /* unknown */ debug("aic7xxx_isr: seqint, intstat = 0x%x, scsisigi = 0x%x\n", intstat, inb(SCSISIGI(base))); @@ -2074,8 +2691,8 @@ { int status = inb(SSTAT1(base)); - scbptr = inb(SCBPTR(base)); - scb = &p->scb_array[scbptr]; + scb_index = inb(SCBPTR(base)); + scb = &(p->scb_array[scb_index]); if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL)) { printk("aic7xxx_isr: no command for scb (scsiint)\n"); @@ -2128,13 +2745,11 @@ else { active = inb(HA_ACTIVE0(base)); - active = active & ~(target_mask); + active &= ~(target_mask); outb(active, HA_ACTIVE0(base)); } -#ifdef AIC7XXX_USE_DMA outb(SCB_NEEDDMA, SCBARRAY(base)); -#endif /* * Shut off the offending interrupt sources, reset @@ -2155,32 +2770,16 @@ outb(CLRSCSIINT, CLRINT(base)); - /* Shift the waiting for selection queue forward */ + /* + * Shift the waiting for selection queue forward + */ waiting = inb(WAITING_SCBH(base)); outb(waiting, SCBPTR(base)); waiting = inb(SCBARRAY(base) + 30); outb(waiting, WAITING_SCBH(base)); RESTART_SEQUENCER(p); - /* - * This is a critical section, since we don't want the - * queue routine mucking with the host data. - */ - save_flags(flags); - cli(); - - /* - * Process the command after marking the scb as free - * and adding it to the free list. - */ - scb->state = SCB_FREE; - scb->cmd = NULL; - scb->next = p->free_scb; /* preserve next pointer */ - p->free_scb = scb; /* add at head of list */ - - restore_flags(flags); - - cmd->scsi_done(cmd); + aic7xxx_done(p, scb); #if 0 printk("aic7xxx_isr: SELTO scb(%d) state(%x), cmd(%x)\n", scb->position, scb->state, (unsigned int) scb->cmd); @@ -2246,9 +2845,9 @@ { printk("aic7xxx warning: " "no command for scb %d (cmdcmplt)\n" - "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x\n", + "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x, pos = %d\n", complete, inb(QOUTFIFO(base)), - scb->state, (unsigned int) scb->cmd); + scb->state, (unsigned int) scb->cmd, scb->position); outb(CLRCMDINT, CLRINT(base)); continue; } @@ -2256,26 +2855,28 @@ cmd->result = (aic7xxx_error(cmd) << 16) | aic7xxx_status(cmd); if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE)) - { /* Got sense information. */ - cmd->flags = cmd->flags & ASKED_FOR_SENSE; + { + /* + * Got sense information. + */ + cmd->flags &= ASKED_FOR_SENSE; } #if 0 printk("aic7xxx_intr: (complete) state = %d, cmd = 0x%x, free = 0x%x\n", scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb); #endif + /* - * This is a critical section, since we don't want the - * queue routine mucking with the host data. + * Clear interrupt status before checking + * the output queue again. This eliminates + * a race condition whereby a command could + * complete between the queue poll and the + * interrupt clearing, so notification of the + * command being complete never made it back + * up to the kernel. */ - save_flags(flags); - cli(); - - scb->state = SCB_FREE; - scb->next = p->free_scb; - scb->cmd = NULL; - p->free_scb = &(p->scb_array[scb->position]); - - restore_flags(flags); + outb(CLRCMDINT, CLRINT(base)); + aic7xxx_done(p, scb); #if 0 if (scb != &p->scb_array[scb->position]) { @@ -2285,18 +2886,48 @@ scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb); #endif - cmd->scsi_done(cmd); - +#ifdef AIC7XXX_PROC_STATS /* - * Clear interrupt status before checking - * the output queue again. This eliminates - * a race condition whereby a command could - * complete between the queue poll and the - * interrupt clearing, so notification of the - * command being complete never made it back - * up to the kernel. + * XXX: we should actually know how much actually transferred + * XXX: for each command, but apparently that's too difficult. */ - outb(CLRCMDINT, CLRINT(base)); + actual = aic7xxx_length(cmd, 0); + if (((cmd->flags & WAS_SENSE) == 0) && (actual > 0)) + { + struct aic7xxx_xferstats *sp; + long *ptr; + int x; + + sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07]; + sp->xfers++; + + if (cmd->request.cmd == WRITE) + { + sp->w_total++; + sp->w_total512 += (actual >> 9); + ptr = sp->w_bins; + } + else + { + sp->r_total++; + sp->r_total512 += (actual >> 9); + ptr = sp->r_bins; + } + for (x = 9; x <= 17; x++) + { + if (actual < (1 << x)) + { + ptr[x - 9]++; + break; + } + } + if (x > 17) + { + ptr[x - 9]++; + } + } +#endif /* AIC7XXX_PROC_STATS */ + } while (inb(QOUTCNT(base))); } } @@ -2449,7 +3080,7 @@ timeout = jiffies + 100; /* 1 second timeout */ while ((jiffies < timeout) && ((inb(SEECTL(base)) & SEERDY) == 0)) { - ; /* Do nothing! Wait for access to be granted. */ + ; /* Do nothing! Wait for access to be granted. */ } if ((inb(SEECTL(base)) & SEERDY) == 0) { @@ -2465,7 +3096,9 @@ */ for (k = 0; k < (sizeof(*sc) / 2); k++) { - /* Send chip select for one clock cycle. */ + /* + * Send chip select for one clock cycle. + */ outb(SEEMS | SEECK | SEECS, SEECTL(base)); CLOCK_PULSE(base); @@ -2482,7 +3115,9 @@ outb(temp, SEECTL(base)); CLOCK_PULSE(base); } - /* Send the 6 bit address (MSB first, LSB last). */ + /* + * Send the 6 bit address (MSB first, LSB last). + */ for (i = 5; i >= 0; i--) { temp = k; @@ -2523,7 +3158,9 @@ checksum = checksum + seeprom[k]; } - /* Reset the chip select for the next command cycle. */ + /* + * Reset the chip select for the next command cycle. + */ outb(SEEMS, SEECTL(base)); CLOCK_PULSE(base); outb(SEEMS | SEECK, SEECTL(base)); @@ -2532,11 +3169,10 @@ CLOCK_PULSE(base); } - if (checksum != sc->checksum) - { - printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n"); - return (0); - } + /* + * Release access to the memory port and the serial EEPROM. + */ + outb(0, SEECTL(base)); #if 0 printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum); @@ -2552,8 +3188,12 @@ printk("\n"); #endif - /* Release access to the memory port and the serial EEPROM. */ - outb(0, SEECTL(base)); + if (checksum != sc->checksum) + { + printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n"); + return (0); + } + return (1); } @@ -2586,7 +3226,10 @@ sblkctl_reg = inb(SBLKCTL(base)) ^ AUTOFLUSHDIS; outb(sblkctl_reg, SBLKCTL(base)); if (inb(SBLKCTL(base)) == sblkctl_reg) - { /* We detected a Rev E board. */ + { + /* + * We detected a Rev E board. + */ printk("aic7770: Rev E and subsequent; using 4 SCB's\n"); outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL(base)); maxscb = 4; @@ -2641,6 +3284,7 @@ unsigned char sblkctl; int max_targets; int found = 1; + int bios_disabled = 0; unsigned char target_settings; unsigned char scsi_conf, host_conf; int have_seeprom = 0; @@ -2713,6 +3357,10 @@ * since there was some issue about reseting the board. */ config.irq = inb(HA_INTDEF(config.base)) & 0x0F; + if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED) + { + bios_disabled = 1; + } host_conf = inb(HA_HOSTCONF(config.base)); config.busrtime = host_conf & 0x3C; /* XXX Is this valid for motherboard based controllers? */ @@ -2736,6 +3384,10 @@ config.pause = REQ_PAUSE; /* DWG would like to be like the rest */ config.extended = aic7xxx_extended; config.irq = inb(HA_INTDEF(config.base)) & 0x0F; + if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED) + { + bios_disabled = 1; + } host_conf = inb(HA_HOSTCONF(config.base)); config.busrtime = host_conf & 0x3C; /* XXX Is this valid for motherboard based controllers? */ @@ -2772,7 +3424,7 @@ else { printk("done\n"); - config.extended = (sc.bios_control & CFEXTEND) >> 7; + config.extended = ((sc.bios_control & CFEXTEND) >> 7); config.scsi_id = (sc.brtime_id & CFSCSIID); config.parity = (sc.adapter_control & CFSPARITY) ? AIC_ENABLED : AIC_DISABLED; @@ -2780,7 +3432,7 @@ AIC_ENABLED : AIC_DISABLED; config.high_term = (sc.adapter_control & CFWSTERM) ? AIC_ENABLED : AIC_DISABLED; - config.busrtime = (sc.brtime_id & CFBRTIME) >> 8; + config.busrtime = ((sc.brtime_id & CFBRTIME) >> 8); } /* @@ -2827,13 +3479,13 @@ sblkctl = inb(SBLKCTL(base)) & 0x0F; /* mask out upper two bits */ switch (sblkctl) { - case 0: /* narrow/normal bus */ + case SELSINGLE: /* narrow/normal bus */ config.scsi_id = inb(HA_SCSICONF(base)) & 0x07; config.bus_type = AIC_SINGLE; - outb(0, HA_FLAGS(base)); + outb(SINGLE_BUS, HA_FLAGS(base)); break; - case 2: /* Wide bus */ + case SELWIDE: /* Wide bus */ config.scsi_id = inb(HA_SCSICONF(base) + 1) & 0x0F; config.bus_type = AIC_WIDE; printk("aic7xxx: Enabling wide channel of %s-Wide\n", @@ -2841,7 +3493,7 @@ outb(WIDE_BUS, HA_FLAGS(base)); break; - case 8: /* Twin bus */ + case SELBUSB: /* Twin bus */ config.scsi_id = inb(HA_SCSICONF(base)) & 0x07; #ifdef AIC7XXX_TWIN_SUPPORT config.scsi_id_b = inb(HA_SCSICONF(base) + 1) & 0x07; @@ -2940,11 +3592,7 @@ */ host = scsi_register(template, sizeof(struct aic7xxx_host)); host->can_queue = config.maxscb; -#ifdef AIC7XXX_TAGGED_QUEUEING - host->cmd_per_lun = 2; -#else - host->cmd_per_lun = 1; -#endif + host->cmd_per_lun = AIC7XXX_CMDS_PER_LUN; host->this_id = config.scsi_id; host->irq = config.irq; if (config.bus_type == AIC_WIDE) @@ -2958,7 +3606,9 @@ p = (struct aic7xxx_host *) host->hostdata; - /* Initialize the scb array by setting the state to free. */ + /* + * Initialize the scb array by setting the state to free. + */ for (i = 0; i < AIC7XXX_MAXSCB; i++) { p->scb_array[i].state = SCB_FREE; @@ -3025,10 +3675,12 @@ printk("aic7xxx: Downloading sequencer code.."); aic7xxx_loadseq(base); - /* Set Fast Mode and Enable the board */ + /* + * Set Fast Mode and Enable the board + */ outb(FASTMODE, SEQCTL(base)); - if ((p->type == AIC_274x || p->type == AIC_284x)) + if ((p->type == AIC_274x) || (p->type == AIC_284x)) { outb(ENABLE, BCTL(base)); } @@ -3046,18 +3698,20 @@ */ outb(config.scsi_id_b, SCSIID(base)); scsi_conf = inb(HA_SCSICONF(base) + 1) & (ENSPCHK | STIMESEL); - scsi_conf = scsi_conf | ENSTIMER | ACTNEGEN | STPWEN; - outb(scsi_conf, SXFRCTL1(base)); + outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base)); outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base)); - /* Select Channel A */ - outb(0, SBLKCTL(base)); + /* + * Select Channel A + */ + outb(SELSINGLE, SBLKCTL(base)); } outb(config.scsi_id, SCSIID(base)); scsi_conf = inb(HA_SCSICONF(base)) & (ENSPCHK | STIMESEL); outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base)); outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base)); - /* Look at the information that board initialization or the board + /* + * Look at the information that board initialization or the board * BIOS has left us. In the lower four bits of each target's * scratch space any value other than 0 indicates that we should * initiate synchronous transfers. If it's zero, the user or the @@ -3076,19 +3730,43 @@ { max_targets = 16; } + /* + * Grab the disconnection disable table and invert it for our needs + */ + if (have_seeprom) + { + p->discenable = 0; + } + else + { + if (bios_disabled) + { + printk("aic7xxx : Host Adapter Bios disabled. Using default SCSI " + "device parameters\n"); + p->discenable = 0xFFFF; + } + else + { + p->discenable = ~(inw(HA_DISC_DSB(base))); + } + } for (i = 0; i < max_targets; i++) { if (have_seeprom) { - target_settings = (sc.device_flags[i] & CFXFER) << 4; + target_settings = ((sc.device_flags[i] & CFXFER) << 4); if (sc.device_flags[i] & CFSYNCH) { - p->needsdtr_copy = p->needsdtr_copy | (0x01 << i); + p->needsdtr_copy |= (0x01 << i); } if ((sc.device_flags[i] & CFWIDEB) && (p->bus_type == AIC_WIDE)) { - p->needwdtr_copy = p->needwdtr_copy | (0x01 << i); + p->needwdtr_copy |= (0x01 << i); + } + if (sc.device_flags[i] & CFDISC) + { + p->discenable |= (0x01 << i); } } else @@ -3096,11 +3774,11 @@ target_settings = inb(HA_TARG_SCRATCH(base) + i); if (target_settings & 0x0F) { - p->needsdtr_copy = p->needsdtr_copy | (0x01 << i); + p->needsdtr_copy |= (0x01 << i); /* * Default to asynchronous transfers (0 offset) */ - target_settings = target_settings & 0xF0; + target_settings &= 0xF0; } /* * If we are not wide, forget WDTR. This makes the driver @@ -3109,8 +3787,8 @@ */ if ((target_settings & 0x80) && (p->bus_type == AIC_WIDE)) { - p->needwdtr_copy = p->needwdtr_copy | (0x01 << i); - target_settings = target_settings & 0x7F; + p->needwdtr_copy |= (0x01 << i); + target_settings &= 0x7F; } } outb(target_settings, (HA_TARG_SCRATCH(base) + i)); @@ -3121,7 +3799,7 @@ #if 0 printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr); printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr); -#endif 0 +#endif /* * Clear the control byte for every SCB so that the sequencer @@ -3145,9 +3823,11 @@ outb(0, HA_ACTIVE0(base)); outb(0, HA_ACTIVE1(base)); - /* We don't have any waiting selections */ - outb (SCB_LIST_NULL, WAITING_SCBH(base)); - outb (SCB_LIST_NULL, WAITING_SCBT(base)); + /* + * We don't have any waiting selections + */ + outb(SCB_LIST_NULL, WAITING_SCBH(base)); + outb(SCB_LIST_NULL, WAITING_SCBT(base)); /* * Reset the SCSI bus. Is this necessary? @@ -3170,14 +3850,14 @@ /* * Select channel B. */ - outb(2, SBLKCTL(base)); + outb(SELBUSB, SBLKCTL(base)); outb(SCSIRSTO, SCSISEQ(base)); udelay(1000); outb(0, SCSISEQ(base)); /* * Select channel A. */ - outb(0, SBLKCTL(base)); + outb(SELSINGLE, SBLKCTL(base)); } outb(SCSIRSTO, SCSISEQ(base)); @@ -3390,7 +4070,7 @@ */ aic7xxx_spurious_count = 0; - index += 1; + index++; } } } @@ -3414,8 +4094,8 @@ struct aic7xxx_scb *scb) { void *addr; - unsigned length; unsigned short mask; + struct scatterlist *sg; /* * Setup the control byte if we need negotiation and have not @@ -3432,15 +4112,19 @@ cmd->device->current_tag = 1; /* enable tagging */ } cmd->tag = cmd->device->current_tag; - cmd->device->current_tag = cmd->device->current_tag + 1; - scb->control = scb->control | SCB_TE; + cmd->device->current_tag++; + scb->control |= SCB_TE; } #endif - mask = (0x01 << cmd->target); + mask = (0x01 << (cmd->target | (cmd->channel << 3))); + if (p->discenable & mask) + { + scb->control |= SCB_DISCENB; + } if ((p->needwdtr & mask) && !(p->wdtr_pending & mask)) { - p->wdtr_pending = p->wdtr_pending | mask; - scb->control = scb->control | SCB_NEEDWDTR; + p->wdtr_pending |= mask; + scb->control |= SCB_NEEDWDTR; #if 0 printk("Sending WDTR request to target %d.\n", cmd->target); #endif @@ -3449,8 +4133,8 @@ { if ((p->needsdtr & mask) && !(p->sdtr_pending & mask)) { - p->sdtr_pending = p->sdtr_pending | mask; - scb->control = scb->control | SCB_NEEDSDTR; + p->sdtr_pending |= mask; + scb->control |= SCB_NEEDSDTR; #if 0 printk("Sending SDTR request to target %d.\n", cmd->target); #endif @@ -3469,17 +4153,7 @@ * changes depending on whether or not use_sg is zero; a * non-zero use_sg indicates the number of elements in the * scatter-gather array. - * - * The AIC-7770 can't support transfers of any sort larger - * than 2^24 (three-byte count) without backflips. For what - * the kernel is doing, this shouldn't occur. I hope. */ - length = aic7xxx_length(cmd, 0); - - if (length > 0xFFFFFF) - { - panic("aic7xxx_buildscb: can't transfer > 2^24 - 1 bytes\n"); - } /* * XXX - this relies on the host data being stored in a @@ -3493,29 +4167,47 @@ { #if 0 debug("aic7xxx_buildscb: SG used, %d segments, length %u\n", - cmd->use_sg, length); + cmd->use_sg, length); #endif scb->SG_segment_count = cmd->use_sg; memcpy(scb->SG_list_pointer, &cmd->request_buffer, sizeof(scb->SG_list_pointer)); + memcpy(&sg, &cmd->request_buffer, sizeof(sg)); + memcpy(scb->data_pointer, &(sg[0].address), sizeof(scb->data_pointer)); + scb->data_count[0] = sg[0].length & 0xFF; + scb->data_count[1] = (sg[0].length >> 8) & 0xFF; + scb->data_count[2] = (sg[0].length >> 16) & 0xFF; } else { #if 0 - debug("aic7xxx_buildscb: Creating scatterlist, addr=0x%lx, length=%d.\n", - (unsigned long) cmd->request_buffer, cmd->request_bufflen); -#endif -#ifdef AIC7XXX_USE_SG - scb->SG_segment_count = 1; - scb->sg.address = (char *) cmd->request_buffer; - scb->sg.length = cmd->request_bufflen; - addr = &scb->sg; - memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); -#else - scb->SG_segment_count = 0; - memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer)); - memcpy(scb->data_count, &cmd->request_bufflen, 3); + debug("aic7xxx_buildscb: Creating scatterlist, addr=0x%lx, length=%d.\n", + (unsigned long) cmd->request_buffer, cmd->request_bufflen); #endif + if (cmd->request_bufflen == 0) + { + /* + * In case the higher level SCSI code ever tries to send a zero + * length command, ensure the SCB indicates no data. The driver + * will interpret a zero length command as a Bus Device Reset. + */ + scb->SG_segment_count = 0; + memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); + memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); + memset(scb->data_count, 0, sizeof(scb->data_count)); + } + else + { + scb->SG_segment_count = 1; + scb->sg.address = (char *) cmd->request_buffer; + scb->sg.length = cmd->request_bufflen; + addr = &scb->sg; + memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); + scb->data_count[0] = scb->sg.length & 0xFF; + scb->data_count[1] = (scb->sg.length >> 8) & 0xFF; + scb->data_count[2] = (scb->sg.length >> 16) & 0xFF; + memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer)); + } } } @@ -3530,17 +4222,16 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) { long flags; -#ifndef AIC7XXX_USE_DMA - int old_scbptr; -#endif struct aic7xxx_host *p; struct aic7xxx_scb *scb; unsigned char curscb; p = (struct aic7xxx_host *) cmd->host->hostdata; - /* Check to see if channel was scanned. */ - if (!p->a_scanned && (cmd->channel == 0)) + /* + * Check to see if channel was scanned. + */ + if (!p->a_scanned && (cmd->channel == 0)) { printk("aic7xxx: Scanning channel A for devices.\n"); p->a_scanned = 1; @@ -3615,13 +4306,11 @@ scb->state = SCB_ACTIVE; scb->next_waiting = SCB_LIST_NULL; memcpy(scb->host_scb, &scb, sizeof(scb)); -#ifdef AIC7XXX_USE_DMA scb->control = SCB_NEEDDMA; -#endif PAUSE_SEQUENCER(p); curscb = inb(SCBPTR(p->base)); outb(scb->position, SCBPTR(p->base)); - aic7xxx_putdmascb(p->base, scb); + aic7xxx_putscb_dma(p->base, scb); outb(curscb, SCBPTR(p->base)); UNPAUSE_SEQUENCER(p); scb->control = 0; @@ -3630,6 +4319,9 @@ scb->cmd = cmd; aic7xxx_position(cmd) = scb->position; +#if 0 + debug_scb(scb); +#endif; /* * Construct the SCB beforehand, so the sequencer is @@ -3660,17 +4352,8 @@ * the SCB, then write its pointer into the queue in FIFO * and restore the saved SCB pointer. */ -#ifdef AIC7XXX_USE_DMA - aic7xxx_putscb(p->base, scb); -#else - old_scbptr = inb(SCBPTR(p->base)); - outb(scb->position, SCBPTR(p->base)); - aic7xxx_putscb(p->base, scb); - outb(scb->position, QINFIFO(p->base)); - outb(old_scbptr, SCBPTR(p->base)); -#endif /* * Make sure the Scsi_Cmnd pointer is saved, the struct it * points to is set up properly, and the parity error flag @@ -3680,205 +4363,149 @@ cmd->scsi_done = fn; aic7xxx_error(cmd) = DID_OK; aic7xxx_status(cmd) = 0; - + scb->timer_status = 0x0; cmd->result = 0; memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); UNPAUSE_SEQUENCER(p); +#if 0 + printk("aic7xxx_queue: After - cmd = 0x%lx, scb->cmd = 0x%lx, pos = %d\n", + (long) cmd, (long) scb->cmd, scb->position); +#endif; restore_flags(flags); return (0); } -/* return values from aic7xxx_kill */ -typedef enum { - k_ok, /* scb found and message sent */ - k_busy, /* message already present */ - k_absent, /* couldn't locate scb */ - k_disconnect, /* scb found, but disconnected */ -} k_state; - /*+F************************************************************************* * Function: - * aic7xxx_kill + * aic7xxx_abort_scb * * Description: - * This must be called with interrupts disabled - it's going to - * be messing around with the host data, and an interrupt being - * fielded in the middle could get ugly. - * - * Since so much of the abort and reset code is shared, this - * function performs more magic than it really should. If the - * command completes ok, then it will call scsi_done with the - * result code passed in. The unpause parameter controls whether - * or not the sequencer gets unpaused - the reset function, for - * instance, may want to do something more aggressive. - * - * Note that the command is checked for in our SCB_array first - * before the sequencer is paused, so if k_absent is returned, - * then the sequencer is NOT paused. + * Abort an scb. If the scb has not previously been aborted, then + * we attempt to send a BUS_DEVICE_RESET message to the target. If + * the scb has previously been unsuccessfully aborted, then we will + * reset the channel and have all devices renegotiate. *-F*************************************************************************/ -static k_state -aic7xxx_kill(Scsi_Cmnd *cmd, unsigned char message, - unsigned int result, int unpause) +static void +aic7xxx_abort_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - struct aic7xxx_host *p; - struct aic7xxx_scb *scb; - int i, active_scb, found, queued; - unsigned char scbsave[AIC7XXX_MAXSCB]; - unsigned char flags; - int scb_control; - k_state status; - - p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = &p->scb_array[aic7xxx_position(cmd)]; - -#if 0 - printk("aic7xxx_kill: In the kill function...\n"); -#endif - PAUSE_SEQUENCER(p); + int base = p->base; + int found = 0; + char channel = scb->target_channel_lun & SELBUSB ? 'B': 'A'; /* - * Case 1: In the QINFIFO - * - * This is the best case, really. Check to see if the - * command is still in the sequencer's input queue. If - * so, simply remove it. Reload the queue afterward. + * Ensure that the card doesn't do anything + * behind our back. */ - queued = inb(QINCNT(p->base)); - - for (i = found = 0; i < (queued - found); i++) - { - scbsave[i] = inb(QINFIFO(p->base)); - - if (scbsave[i] == scb->position) - { - found = 1; - i--; - } - } - - for (queued = 0; queued < i; queued++) - { - outb(scbsave[queued], QINFIFO(p->base)); - } - - if (found) - { - status = k_ok; - goto complete; - } + PAUSE_SEQUENCER(p); - active_scb = inb(SCBPTR(p->base)); /* - * Case 2: Not the active command - * - * Check the current SCB bank. If it's not the one belonging - * to the command we want to kill, select the scb we want to - * abort and turn off the disconnected bit. The driver will - * then abort the command and notify us of the abort. + * First, determine if we want to do a bus reset or simply a bus device + * reset. If this is the first time that a transaction has timed out, + * just schedule a bus device reset. Otherwise, we reset the bus and + * abort all pending I/Os on that bus. */ - if (active_scb != scb->position) + if (scb->state & SCB_ABORTED) { - outb(scb->position, SCBPTR(p->base)); - scb_control = inb(SCBARRAY(p->base)); - scb_control = scb_control & ~SCB_DIS; - outb(scb_control, SCBARRAY(p->base)); - outb(active_scb, SCBPTR(p->base)); - status = k_disconnect; - goto complete; + /* + * Been down this road before. Do a full bus reset. + */ + found = aic7xxx_reset_channel(p, channel, scb->position); } - - scb_control = inb(SCBARRAY(p->base)); - if (scb_control & SCB_DIS) + else { - scb_control = scb_control & ~SCB_DIS; - outb(scb_control, SCBARRAY(p->base)); - status = k_disconnect; - goto complete; - } + unsigned char active_scb, control; + struct aic7xxx_scb *active_scbp; - /* - * Presumably at this point our target command is active. Check - * to see if there's a message already in effect. If not, place - * our message in and assert ATN so the target goes into MESSAGE - * OUT phase. - */ - flags = inb(HA_FLAGS(p->base)); - if (flags & ACTIVE_MSG) - { /* - * If there is a message in progress, reset the bus - * and have all devices renegotiate. + * Send a Bus Device Reset Message: + * The target we select to send the message to may be entirely + * different than the target pointed to by the scb that timed + * out. If the command is in the QINFIFO or the waiting for + * selection list, its not tying up the bus and isn't responsible + * for the delay so we pick off the active command which should + * be the SCB selected by SCBPTR. If its disconnected or active, + * we device reset the target scbp points to. Although it may + * be that this target is not responsible for the delay, it may + * may also be that we're timing out on a command that just takes + * too much time, so we try the bus device reset there first. */ - if (cmd->channel & 0x01) + active_scb = inb(SCBPTR(base)); + active_scbp = &(p->scb_array[active_scb]); + control = inb(SCBARRAY(base)); + + /* + * Test to see if scbp is disconnected + */ + outb(scb->position, SCBPTR(base)); + if (inb(SCBARRAY(base)) & SCB_DIS) { - p->needsdtr = p->needsdtr_copy & 0xFF00; - p->sdtr_pending = p->sdtr_pending & 0x00FF; - outb(0, HA_ACTIVE1(p->base)); + scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED); + scb->SG_segment_count = 0; + memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); + memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); + memset(scb->data_count, 0, sizeof(scb->data_count)); + outb(SCBAUTO, SCBCNT(base)); + asm volatile("cld\n\t" + "rep\n\t" + "outsb" + : /* no output */ + :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base)) + :"si", "cx", "dx"); + outb(0, SCBCNT(base)); + aic7xxx_add_waiting_scb(base, scb, LIST_SECOND); + aic7xxx_scb_tsleep(p, scb, 2 * HZ); /* unpauses the sequencer */ } else { - if (p->bus_type == AIC_WIDE) + /* + * Is the active SCB really active? + */ + if ((active_scbp->state & SCB_ACTIVE) && (control & SCB_NEEDDMA)) { - p->needsdtr = p->needsdtr_copy; - p->needwdtr = p->needwdtr_copy; - p->sdtr_pending = 0; - p->wdtr_pending = 0; - outb(0, HA_ACTIVE0(p->base)); - outb(0, HA_ACTIVE1(p->base)); + unsigned char flags = inb(HA_FLAGS(base)); + if (flags & ACTIVE_MSG) + { + /* + * If we're in a message phase, tacking on another message + * may confuse the target totally. The bus is probably wedged, + * so reset the channel. + */ + channel = (active_scbp->target_channel_lun & SELBUSB) ? 'B': 'A'; + aic7xxx_reset_channel(p, channel, scb->position); + } + else + { + /* + * Load the message buffer and assert attention. + */ + active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED); + outb(flags | ACTIVE_MSG, HA_FLAGS(base)); + outb(1, HA_MSG_LEN(base)); + outb(MSG_BUS_DEVICE_RESET, HA_MSG_START(base)); + if (active_scbp->target_channel_lun != scb->target_channel_lun) + { + /* + * XXX - We would like to increment the timeout on scb, but + * access to that routine is denied because it is hidden + * in scsi.c. If we were able to do this, it would give + * scb a new lease on life. + */ + ; + } + aic7xxx_scb_tsleep(p, active_scbp, 2 * HZ); + } } else { - p->needsdtr = p->needsdtr_copy & 0x00FF; - p->sdtr_pending = p->sdtr_pending & 0xFF00; - outb(0, HA_ACTIVE0(p->base)); + /* + * No active command to single out, so reset + * the bus for the timed out target. + */ + aic7xxx_reset_channel(p, channel, scb->position); } } - /* Reset the bus. */ - outb(SCSIRSTO, SCSISEQ(p->base)); - udelay(1000); - outb(0, SCSISEQ(p->base)); - aic7xxx_delay(AIC7XXX_RESET_DELAY); - - status = k_busy; - goto complete; - } - - outb(flags | ACTIVE_MSG, HA_FLAGS(p->base)); /* active message */ - outb(1, HA_MSG_LEN(p->base)); /* length = 1 */ - outb(message, HA_MSG_START(p->base)); /* message body */ - - /* - * Assert ATN. Use the value of SCSISIGO saved by the - * sequencer code so we don't alter its contents radically - * in the middle of something critical. - */ - outb(inb(HA_SIGSTATE(p->base)) | 0x10, SCSISIGO(p->base)); - - status = k_ok; - - /* - * The command has been killed. Do the bookkeeping, unpause - * the sequencer, and notify the higher-level SCSI code. - */ -complete: - if (unpause) - { - UNPAUSE_SEQUENCER(p); } - - /* - * Mark the scb as free and clear the scbs command pointer. - * Add the scb to the head of the free list being careful - * to preserve the next pointers. - */ - scb->state = SCB_FREE; /* mark the scb as free */ - scb->cmd = NULL; /* clear the command pointer */ - scb->next = p->free_scb; /* preserve next pointer */ - p->free_scb = scb; /* add at head of free list */ - cmd->result = cmd->result << 16; - cmd->scsi_done(cmd); - return (status); } /*+F************************************************************************* @@ -3891,23 +4518,20 @@ int aic7xxx_abort(Scsi_Cmnd *cmd) { - int rv; + struct aic7xxx_scb *scb; + struct aic7xxx_host *p; long flags; + p = (struct aic7xxx_host *) cmd->host->hostdata; + scb = &(p->scb_array[aic7xxx_position(cmd)]); + save_flags(flags); cli(); - switch (aic7xxx_kill(cmd, ABORT, DID_ABORT, !0)) - { - case k_ok: rv = SCSI_ABORT_SUCCESS; break; - case k_busy: rv = SCSI_ABORT_BUSY; break; - case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break; - case k_disconnect: rv = SCSI_ABORT_SNOOZE; break; - default: panic("aic7xxx_abort: internal error\n"); - } + aic7xxx_abort_scb(p, scb); restore_flags(flags); - return (rv); + return (0); } /*+F************************************************************************* @@ -3923,76 +4547,7 @@ int aic7xxx_reset(Scsi_Cmnd *cmd) { - long flags; - struct aic7xxx_host *p; - - p = (struct aic7xxx_host *) cmd->host->hostdata; - save_flags(flags); - cli(); - - switch (aic7xxx_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) - { - case k_ok: - /* - * The RESET message was sent to the target - * with no problems. Flag that target as - * needing a SDTR negotiation on the next - * connection and restart the sequencer. - */ - p->needsdtr = p->needsdtr & (1 << cmd->target); - UNPAUSE_SEQUENCER(p); - break; - - case k_absent: - /* - * The sequencer will not be paused if aic7xxx_kill() - * couldn't find the command. - */ - PAUSE_SEQUENCER(p); - /* falls through */ - - case k_busy: - cmd->result = DID_RESET << 16; /* return reset code */ - cmd->scsi_done(cmd); - break; - - case k_disconnect: - /* - * Do a hard reset of the SCSI bus. According to the - * SCSI-2 draft specification, reset has to be asserted - * for at least 25us. I'm invoking the kernel delay - * function for 30us since I'm not totally trusting of - * the busy loop timing. - * - * XXX - I'm not convinced this works. I tried resetting - * the bus before, trying to get the devices on the - * bus to revert to asynchronous transfer, and it - * never seemed to work. - */ - debug("aic7xxx: attempting to reset scsi bus and card\n"); - - outb(SCSIRSTO, SCSISEQ(p->base)); - udelay(1000); - outb(0, SCSISEQ(p->base)); - aic7xxx_delay(AIC7XXX_RESET_DELAY); - - UNPAUSE_SEQUENCER(p); - - /* - * Locate the command and return a "reset" status - * for it. This is not completely correct and will - * probably return to haunt me later. - */ - cmd->result = DID_RESET << 16; /* return reset code */ - cmd->scsi_done(cmd); - break; - - default: - panic("aic7xxx_reset: internal error\n"); - } - - restore_flags(flags); - return (SCSI_RESET_SUCCESS); + return (aic7xxx_abort(cmd)); } /*+F************************************************************************* @@ -4032,6 +4587,8 @@ return (0); } + +#include "aic7xxx_proc.c" #ifdef MODULE /* Eventually this will go into an include file, but this will be later */ diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h --- v1.3.35/linux/drivers/scsi/aic7xxx.h Mon Sep 18 14:53:56 1995 +++ linux/drivers/scsi/aic7xxx.h Mon Oct 23 13:21:53 1995 @@ -18,12 +18,12 @@ * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: aic7xxx.h,v 2.0 1995/08/02 05:28:42 deang Exp $ + * $Id: aic7xxx.h,v 2.2 1995/09/20 05:18:18 deang Exp $ *-M*************************************************************************/ #ifndef _aic7xxx_h #define _aic7xxx_h -#define AIC7XXX_H_VERSION "$Revision: 2.0 $" +#define AIC7XXX_H_VERSION "$Revision: 2.2 $" /* * Scsi_Host_Template (see hosts.h) for AIC-7770/AIC-7870 - some fields @@ -33,7 +33,7 @@ NULL, \ NULL, \ NULL, \ - NULL, \ + aic7xxx_proc_info, \ NULL, \ aic7xxx_detect, \ NULL, \ @@ -61,5 +61,7 @@ extern int aic7xxx_reset(Scsi_Cmnd *); extern const char *aic7xxx_info(struct Scsi_Host *); + +extern int aic7xxx_proc_info(char *, char **, off_t, int, int, int); #endif /* _aic7xxx_h */ diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq --- v1.3.35/linux/drivers/scsi/aic7xxx.seq Wed Aug 2 13:21:07 1995 +++ linux/drivers/scsi/aic7xxx.seq Mon Oct 23 13:21:53 1995 @@ -25,9 +25,9 @@ # optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org) ##-M######################################################################### -VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.0 1995/08/02 05:28:42 deang Exp $" +VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.1 1995/08/30 07:47:07 deang Exp $" -SCBMASK = 0x1f +SCBMASK = 0xff SCSISEQ = 0x00 ENRSELI = 0x10 @@ -48,6 +48,7 @@ SELDI = 0x20 CLRSINT1 = 0x0c SSTAT1 = 0x0c +PHASEMIS = 0x10 SIMODE1 = 0x11 SCSIBUSL = 0x12 SHADDR = 0x14 @@ -83,18 +84,19 @@ SCSICONF_B = 0x5b # The two reserved bytes at SCBARRAY+1[23] are expected to be set to -# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag -# to indicate whether or not to reload scatter-gather parameters after -# a disconnect. We also use bits 6 & 7 to indicate whether or not to -# initiate SDTR or WDTR repectively when starting this command. +# zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate +# whether or not to DMA an SCB from host ram. This flag prevents the +# "re-fetching" of transactions that are requed because the target is +# busy with another command. We also use bits 6 & 7 to indicate whether +# or not to initiate SDTR or WDTR repectively when starting this command. # SCBARRAY+0 = 0xa0 DISCONNECTED = 0x04 NEEDDMA = 0x08 -SG_LOAD = 0x10 +NEEDSDTR = 0x10 TAG_ENB = 0x20 -NEEDSDTR = 0x40 +DISCENB = 0x40 NEEDWDTR = 0x80 SCBARRAY+1 = 0xa1 @@ -144,6 +146,7 @@ # (command was null), so tell # it that it can fill the # message buffer. +IMMEDDONE = 0xb1 # The host adapter card (at least the BIOS) uses 20-2f for SCSI @@ -153,11 +156,11 @@ # scratchspace (actually a value that can be copied directly into # SCSIRATE). The kernel driver will enable synchronous negotiation # for all targets that have a value other than 0 in the lower four -# bits of the target scratch space. This should work irregardless of -# whether the bios has been installed. NEEDWDTR and NEEDSDTR are the top -# two bits of the SCB control byte. The kernel driver will set these -# when a WDTR or SDTR message should be sent to the target the SCB's -# command references. +# bits of the target scratch space. This should work regardless of +# whether the bios has been installed. NEEDSDTR and NEEDWDTR are the +# fouth and sevent bits of the SCB control byte. The kernel driver +# will set these when a WDTR or SDTR message should be sent to the +# target the SCB's command references. # # REJBYTE contains the first byte of a MESSAGE IN message, so the driver # can report an intelligible error if a message is rejected. @@ -168,9 +171,9 @@ # no idea what the lun is, and we can't select the right SCB register # bank, so force a kernel panic if the target attempts a data in/out or # command phase instead of corrupting something. FLAGS also contains -# configuration bits so that we can optimize for TWIN and WIDE controllers -# as well as the MAX_OFFSET bit which we set when we want to negotiate for -# maximum sync offset irregardless of what the per target scratch space says. +# configuration bits so that we can optimize for TWIN and WIDE controllers, +# the MAX_OFFSET bit which we set when we want to negotiate for maximum sync +# offset irregardless of what the per target scratch space says. # # Note that SG_NEXT occupies four bytes. # @@ -198,13 +201,9 @@ # Linux users should use 0xc (12) for SG_SIZEOF #SG_SIZEOF = 0x8 # sizeof(struct ahc_dma) SG_SIZEOF = 0xc # sizeof(struct scatterlist) -# if AIC7XXX_USE_SG -SCB_SIZEOF = 0x13 # sizeof SCB to DMA (19 bytes) -# else -#SCB_SIZEOF = 0x1a # sizeof SCB without SG -# endif +SCB_SIZEOF = 0x1a # sizeof SCB to DMA (26 bytes) -SG_NOLOAD = 0x4c # load SG pointer/length? +DMAPARAMS = 0x4c # Parameters for DMA SG_COUNT = 0x4d # working value of SG count SG_NEXT = 0x4e # working value of SG pointer SG_NEXT+0 = 0x4e @@ -216,6 +215,7 @@ FLAGS = 0x53 # Device configuration flags TWIN_BUS = 0x01 WIDE_BUS = 0x02 +DPHASE = 0x04 MAX_OFFSET = 0x08 ACTIVE_MSG = 0x20 IDENTIFY_SEEN = 0x40 @@ -240,8 +240,8 @@ # ram since a reconnecting target can request sense and this will create # yet another SCB waiting for selection. The solution used here is to # use byte 31 of the SCB as a psuedo-next pointer and to thread a list -# of SCBs that are awaiting selection. Since 0 is a valid SCB offset, -# SCB_LIST_NULL is 0x10 which is out of range. The kernel driver must +# of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets, +# SCB_LIST_NULL is 0xff which is out of range. The kernel driver must # add an entry to this list everytime a request sense occurs. The sequencer # will automatically consume the entries. @@ -249,26 +249,22 @@ # selection WAITING_SCBT = 0x58 # tail of list of SCBs awaiting # selection -SCB_LIST_NULL = 0x10 +SCB_LIST_NULL = 0xff # Poll QINCNT for work - the lower bits contain # the number of entries in the Queue In FIFO. # -start: - test WAITING_SCBH,SCB_LIST_NULL jz start_waiting poll_for_work: test FLAGS,TWIN_BUS jz start2 # Are we a twin channel device? # For fairness, we check the other bus first, since we just finished a # transaction on the current channel. xor SBLKCTL,0x08 # Toggle to the other bus test SSTAT0,SELDI jnz reselect - test SSTAT0,SELDO jnz select xor SBLKCTL,0x08 # Toggle to the original bus start2: test SSTAT0,SELDI jnz reselect - test SSTAT0,SELDO jnz select - test WAITING_SCBH,SCB_LIST_NULL jz start_waiting + cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting test QINCNT,SCBMASK jz poll_for_work # We have at least one queued SCB now and we don't have any @@ -300,15 +296,10 @@ # Copy the SCB from the FIFO to the SCBARRAY mvi DINDEX, SCBARRAY+0 - call bcopy_3_dfdat - call bcopy_4_dfdat - call bcopy_4_dfdat - call bcopy_4_dfdat - call bcopy_4_dfdat -# ifndef AIC7XXX_USE_SG -# call bcopy_3_dfdat -# call bcopy_4_dfdat -# endif + call bcopy_5_dfdat + call bcopy_7_dfdat + call bcopy_7_dfdat + call bcopy_7_dfdat # See if there is not already an active SCB for this target. This code # locks out on a per target basis instead of target/lun. Although this @@ -320,12 +311,12 @@ # initialization, board reset, and a target's SELTO. test_busy: - test SCBARRAY+0,0x20 jnz start_scb and FUNCTION1,0x70,SCBARRAY+1 mov A,FUNCTION1 test SCBARRAY+1,0x88 jz test_a # Id < 8 && A channel test ACTIVE_B,A jnz requeue + test SCBARRAY+0,TAG_ENB jnz start_scb or ACTIVE_B,A # Mark the current target as busy jmp start_scb @@ -341,6 +332,7 @@ test_a: test ACTIVE_A,A jnz requeue + test SCBARRAY+0,TAG_ENB jnz start_scb or ACTIVE_A,A # Mark the current target as busy start_scb: @@ -358,8 +350,7 @@ start_selection: or SCSISEQ,0x48 # ENSELO|ENAUTOATNO mov WAITING_SCBH, SCBPTR - clr SG_NOLOAD - and FLAGS,0x3f # !RESELECTING + and FLAGS,0x3f # !RESELECTING # As soon as we get a successful selection, the target should go # into the message out phase since we have ATN asserted. Prepare @@ -378,17 +369,17 @@ # so we interrupt the driver, allow it to fill the message buffer, and # then go back into the arbitration loop mvi INTSTAT,AWAITING_MSG - jmp poll_for_work + jmp wait_for_selection identify: - mov SCBARRAY+1 call disconnect # disconnect ok? + and A,DISCENB,SCBARRAY+0 # mask off disconnect privledge and SINDEX,0x7,SCBARRAY+1 # lun - or SINDEX,A # return value from disconnect + or SINDEX,A # or in disconnect privledge or SINDEX,0x80 call mk_mesg # IDENTIFY message mov A,SINDEX - test SCBARRAY+0,0xe0 jz !message # WDTR, SDTR or TAG?? + test SCBARRAY+0,0xb0 jz !message # WDTR, SDTR or TAG?? cmp MSG_START+0,A jne !message # did driver beat us? # Tag Message if Tag enabled in SCB control block. Use SCBPTR as the tag @@ -408,7 +399,10 @@ mov DINDEX call mk_dtr # build DTR message if needed !message: - jmp poll_for_work +wait_for_selection: + test SSTAT0,SELDI jnz reselect + test SSTAT0,SELDO jnz select + jmp wait_for_selection # Reselection has been initiated by a target. Make a note that we've been # reselected, but haven't seen an IDENTIFY message from the target @@ -461,78 +455,118 @@ p_dataout: mvi 0 call scsisig # !CDO|!IOO|!MSGO - call assert - call sg_load + mvi DMAPARAMS,0x7d # WIDEODD|SCSIEN|SDMAEN|HDMAEN| + # DIRECTION|FIFORESET + jmp data_phase_init - mvi DINDEX,HADDR - mvi SCBARRAY+19 call bcopy_4 +# If we re-enter the data phase after going through another phase, the +# STCNT may have been cleared, so restore it from the residual field. +data_phase_reinit: + mvi DINDEX, STCNT + mvi SCBARRAY+15 call bcopy_3 + jmp data_phase_loop -# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR - mvi SCBARRAY+23 call bcopy_3 +# Reads should not use WIDEODD since it may make the last byte for a SG segment +# go to the next segment. +p_datain: + mvi 0x40 call scsisig # !CDO|IOO|!MSGO + mvi DMAPARAMS,0x39 # SCSIEN|SDMAEN|HDMAEN| + # !DIRECTION|FIFORESET +data_phase_init: + call assert - mvi DINDEX,STCNT - mvi SCBARRAY+23 call bcopy_3 + test FLAGS, DPHASE jnz data_phase_reinit + call sg_scb2ram + or FLAGS, DPHASE # We have seen a data phase +data_phase_loop: # If we are the last SG block, don't set wideodd. - test SCBARRAY+18,0xff jnz p_dataout_wideodd - mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET - jmp p_dataout_rest + cmp SG_COUNT,0x01 jne data_phase_wideodd + and DMAPARAMS, 0xbf # Turn off WIDEODD +data_phase_wideodd: + mov DMAPARAMS call dma -p_dataout_wideodd: - mvi 0xbd call dma # WIDEODD|SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET +# Exit if we had an underrun + test SSTAT0,0x04 jz data_phase_finish # underrun STCNT != 0 -p_dataout_rest: -# After a DMA finishes, save the final transfer pointer and count -# back into the SCB, in case a device disconnects in the middle of -# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since -# it's a reflection of how many bytes were transferred on the SCSI -# (as opposed to the host) bus. +# Advance the scatter-gather pointers if needed # - mvi DINDEX,SCBARRAY+23 - mvi STCNT call bcopy_3 - - mvi DINDEX,SCBARRAY+19 - mvi SHADDR call bcopy_4 +sg_advance: + dec SG_COUNT # one less segment to go - call sg_advance - mov SCBARRAY+18,SG_COUNT # residual S/G count + test SG_COUNT, 0xff jz data_phase_finish #Are we done? - jmp ITloop + clr A # add sizeof(struct scatter) + add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0 + adc SG_NEXT+1,A,SG_NEXT+1 + adc SG_NEXT+2,A,SG_NEXT+2 + adc SG_NEXT+3,A,SG_NEXT+3 -p_datain: - mvi 0x40 call scsisig # !CDO|IOO|!MSGO - call assert - call sg_load +# Load a struct scatter and set up the data address and length. +# If the working value of the SG count is nonzero, then +# we need to load a new set of values. +# +# This, like all DMA's, assumes a little-endian host data storage. +# +sg_load: + clr HCNT+2 + clr HCNT+1 + mvi HCNT+0,SG_SIZEOF mvi DINDEX,HADDR - mvi SCBARRAY+19 call bcopy_4 + mvi SG_NEXT call bcopy_4 -# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR - mvi SCBARRAY+23 call bcopy_3 + mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET - mvi DINDEX,STCNT - mvi SCBARRAY+23 call bcopy_3 +# Wait for DMA from host memory to data FIFO to complete, then disable +# DMA and wait for it to acknowledge that it's off. +# + call dma_finish -# If we are the last SG block, don't set wideodd. - test SCBARRAY+18,0xff jnz p_datain_wideodd - mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN| - # !DIRECTION|FIFORESET - jmp p_datain_rest -p_datain_wideodd: - mvi 0xb9 call dma # WIDEODD|SCSIEN|SDMAEN|HDMAEN| - # !DIRECTION|FIFORESET -p_datain_rest: - mvi DINDEX,SCBARRAY+23 - mvi STCNT call bcopy_3 +# Copy data from FIFO into SCB data pointer and data count. This assumes +# that the struct scatterlist has this structure (this and sizeof(struct +# scatterlist) == 12 are asserted in aic7xxx.c): +# +# struct scatterlist { +# char *address; /* four bytes, little-endian order */ +# ... /* four bytes, ignored */ +# unsigned short length; /* two bytes, little-endian order */ +# } +# - mvi DINDEX,SCBARRAY+19 - mvi SHADDR call bcopy_4 +# Not in FreeBSD. the scatter list entry is only 8 bytes. +# +# struct ahc_dma_seg { +# physaddr addr; /* four bytes, little-endian order */ +# long len; /* four bytes, little endian order */ +# }; +# + + mvi DINDEX,HADDR +# call bcopy_7_dfdat + +# For Linux, we must throw away four bytes since there is a 32bit gap +# in the middle of a struct scatterlist + call bcopy_4_dfdat + mov NONE,DFDAT + mov NONE,DFDAT + mov NONE,DFDAT + mov NONE,DFDAT + call bcopy_3_dfdat #Only support 24 bit length. - call sg_advance - mov SCBARRAY+18,SG_COUNT # residual S/G count +# Load STCNT as well. It is a mirror of HCNT + mvi DINDEX,STCNT + mvi HCNT call bcopy_3 + test SSTAT1,PHASEMIS jz data_phase_loop +data_phase_finish: +# After a DMA finishes, save the SG and STCNT residuals back into the SCB +# We use STCNT instead of HCNT, since it's a reflection of how many bytes +# were transferred on the SCSI (as opposed to the host) bus. +# + mvi DINDEX,SCBARRAY+15 + mvi STCNT call bcopy_3 + mov SCBARRAY+18, SG_COUNT jmp ITloop # Command phase. Set up the DMA registers and let 'er rip - the @@ -543,11 +577,9 @@ mvi 0x80 call scsisig # CDO|!IOO|!MSGO call assert +# Load HADDR and HCNT. We can do this in one bcopy since they are neighbors mvi DINDEX,HADDR - mvi SCBARRAY+7 call bcopy_4 - -# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR - mvi SCBARRAY+11 call bcopy_3 + mvi SCBARRAY+7 call bcopy_7 mvi DINDEX,STCNT mvi SCBARRAY+11 call bcopy_3 @@ -563,7 +595,7 @@ mvi 0xc0 call scsisig # CDO|IOO|!MSGO mvi SCBARRAY+14 call inb_first - jmp p_mesgin_done + jmp mesgin_done # Message out phase. If there is no active message, but the target # took us into this phase anyway, build a no-op message and send it. @@ -651,8 +683,34 @@ mvi A call inb_first # read the 1st message byte mvi REJBYTE,A # save it for the driver - cmp ALLZEROS,A jne p_mesgin1 + test A,0x80 jnz mesgin_identify # identify message? + cmp A,4 je mesgin_disconnect # disconnect? + cmp A,2 je mesgin_sdptrs # save data pointers? + cmp ALLZEROS,A je mesgin_complete # command complete? + cmp A,3 je mesgin_rdptrs # restore pointers code? + cmp A,1 je mesgin_extended # extended message? + cmp A,7 je mesgin_reject # message reject code? + +rej_mesgin: +# We have no idea what this message in is, and there's no way +# to pass it up to the kernel, so we issue a message reject and +# hope for the best. Since we're now using manual PIO mode to +# read in the message, there should no longer be a race condition +# present when we assert ATN. In any case, rejection should be a +# rare occurrence - signal the driver when it happens. +# + or SINDEX,0x10,SIGSTATE # turn on ATNO + call scsisig + mvi INTSTAT,SEND_REJECT # let driver know + + mvi 0x7 call mk_mesg # MESSAGE REJECT message + +mesgin_done: + call inb_last # ack & turn auto PIO back on + jmp ITloop + +mesgin_complete: # We got a "command complete" message, so put the SCB pointer # into the Queue Out, and trigger a completion interrupt. # Check status for non zero return and interrupt driver if needed @@ -669,19 +727,17 @@ # before the command complete code tried processing it. # First check for residuals - test SCBARRAY+15,0xff jnz resid - test SCBARRAY+16,0xff jnz resid - test SCBARRAY+17,0xff jnz resid + test SCBARRAY+18,0xff jnz resid check_status: test SCBARRAY+14,0xff jz status_ok # 0 Status? mvi INTSTAT,BAD_STATUS # let driver know test RETURN_1, 0x80 jz status_ok - jmp p_mesgin_done + jmp mesgin_done status_ok: # First, mark this target as free. - test SCBARRAY+0,0x20 jnz complete # Tagged command + test SCBARRAY+0,TAG_ENB jnz complete # Tagged command and FUNCTION1,0x70,SCBARRAY+1 mov A,FUNCTION1 test SCBARRAY+1,0x88 jz clear_a @@ -691,11 +747,16 @@ clear_a: xor ACTIVE_A,A + test SCBARRAY+11,0xff jnz complete # Immediate message complete +# Pause the sequencer until the driver gets around to handling the command +# complete. This is so that any action that might require carefull timing +# with the completion of this command can occur. + mvi INTSTAT,IMMEDDONE + jmp poll_for_work complete: mov QOUTFIFO,SCBPTR mvi INTSTAT,CMDCMPLT - test SCBARRAY+11,0xff jz start # Immediate message complete - jmp p_mesgin_done + jmp mesgin_done # If we have a residual count, interrupt and tell the host. Other # alternatives are to pause the sequencer on all command completes (yuck), @@ -714,21 +775,19 @@ # apparently this can be done after any message in byte, according # to the SCSI-2 spec. # -p_mesgin1: - cmp A,1 jne p_mesgin2 # extended message code? - +mesgin_extended: mvi ARG_1 call inb_next # extended message length mvi A call inb_next # extended message code cmp A,1 je p_mesginSDTR # Syncronous negotiation message cmp A,3 je p_mesginWDTR # Wide negotiation message - jmp p_mesginN + jmp rej_mesgin p_mesginWDTR: - cmp ARG_1,2 jne p_mesginN # extended mesg length = 2 + cmp ARG_1,2 jne rej_mesgin # extended mesg length=2 mvi A call inb_next # Width of bus mvi INTSTAT,MSG_WDTR # let driver know - test RETURN_1,0x80 jz p_mesgin_done# Do we need to send WDTR? + test RETURN_1,0x80 jz mesgin_done# Do we need to send WDTR? # We didn't initiate the wide negotiation, so we must respond to the request and RETURN_1,0x7f # Clear the SEND_WDTR Flag @@ -737,59 +796,53 @@ mvi MSG_START+0 call mk_wdtr # build WDTR message or SINDEX,0x10,SIGSTATE # turn on ATNO call scsisig - jmp p_mesgin_done + jmp mesgin_done p_mesginSDTR: - cmp ARG_1,3 jne p_mesginN # extended mesg length = 3 + cmp ARG_1,3 jne rej_mesgin # extended mesg length=3 mvi ARG_1 call inb_next # xfer period mvi A call inb_next # REQ/ACK offset mvi INTSTAT,MSG_SDTR # call driver to convert - test RETURN_1,0xc0 jz p_mesgin_done# Do we need to mk_sdtr or rej? - test RETURN_1,0x40 jnz p_mesginN # Requested SDTR too small - rej + test RETURN_1,0xc0 jz mesgin_done# Do we need to mk_sdtr or rej? + test RETURN_1,0x40 jnz rej_mesgin # Requested SDTR too small - rej or FLAGS,ACTIVE_MSG mvi DINDEX, MSG_START+0 mvi MSG_START+0 call mk_sdtr or SINDEX,0x10,SIGSTATE # turn on ATNO call scsisig - jmp p_mesgin_done + jmp mesgin_done # Is it a disconnect message? Set a flag in the SCB to remind us # and await the bus going free. # -p_mesgin2: - cmp A,4 jne p_mesgin3 # disconnect code? - - or SCBARRAY+0,0x4 # set "disconnected" bit - jmp p_mesgin_done +mesgin_disconnect: + or SCBARRAY+0,DISCONNECTED + jmp mesgin_done # Save data pointers message? Copy working values into the SCB, # usually in preparation for a disconnect. # -p_mesgin3: - cmp A,2 jne p_mesgin4 # save data pointers code? - +mesgin_sdptrs: call sg_ram2scb - jmp p_mesgin_done + jmp mesgin_done # Restore pointers message? Data pointers are recopied from the -# SCB anyway at the start of any DMA operation, so the only thing -# to copy is the scatter-gather values. -# -p_mesgin4: - cmp A,3 jne p_mesgin5 # restore pointers code? - - call sg_scb2ram - jmp p_mesgin_done +# SCB anytime we enter a data phase for the first time, so all +# we need to do is clear the DPHASE flag and let the data phase +# code do the rest. +# +mesgin_rdptrs: + and FLAGS,0xfb # !DPHASE we'll reload them + # the next time through + jmp mesgin_done # Identify message? For a reconnecting target, this tells us the lun # that the reconnection is for - find the correct SCB and switch to it, # clearing the "disconnected" bit so we don't "find" it by accident later. # -p_mesgin5: - test A,0x80 jz p_mesgin6 # identify message? - - test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved +mesgin_identify: + test A,0x78 jnz rej_mesgin # !DiscPriv|!LUNTAR|!Reserved and A,0x07 # lun in lower three bits or SAVED_TCL,A,SELID @@ -802,8 +855,6 @@ and SCBARRAY+0,0xfb # clear disconnect bit in SCB or FLAGS,IDENTIFY_SEEN # make note of IDENTIFY - call sg_scb2ram # implied restore pointers - # required on reselect jmp ITloop get_tag: mvi A call inb_first @@ -828,34 +879,12 @@ # the target selecting 8bit or asynchronous transfer, otherwise just ignore # it since we have no clue what it pertains to. # -p_mesgin6: - cmp A,7 jne p_mesgin7 # message reject code? - +mesgin_reject: mvi INTSTAT, MSG_REJECT - jmp p_mesgin_done + jmp mesgin_done # [ ADD MORE MESSAGE HANDLING HERE ] # -p_mesgin7: - -# We have no idea what this message in is, and there's no way -# to pass it up to the kernel, so we issue a message reject and -# hope for the best. Since we're now using manual PIO mode to -# read in the message, there should no longer be a race condition -# present when we assert ATN. In any case, rejection should be a -# rare occurrence - signal the driver when it happens. -# -p_mesginN: - or SINDEX,0x10,SIGSTATE # turn on ATNO - call scsisig - mvi INTSTAT,SEND_REJECT # let driver know - - mvi 0x7 call mk_mesg # MESSAGE REJECT message - -p_mesgin_done: - call inb_last # ack & turn auto PIO back on - jmp ITloop - # Bus free phase. It might be useful to interrupt the device # driver if we aren't expecting this. For now, make sure that @@ -868,33 +897,34 @@ # if this is an immediate command, perform a psuedo command complete to # notify the driver. test SCBARRAY+11,0xff jz status_ok - jmp start + jmp poll_for_work # Instead of a generic bcopy routine that requires an argument, we unroll -# the two cases that are actually used, and call them explicitly. This -# not only reduces the overhead of doing a bcopy by 2/3rds, but ends up -# saving space in the program since you don't have to put the argument -# into the accumulator before the call. Both functions expect DINDEX to -# contain the destination address and SINDEX to contain the source -# address. -bcopy_3: +# the cases that are actually used, and call them explicitly. This +# not only reduces the overhead of doing a bcopy, but ends up saving space +# in the program since you don't have to put the argument into the accumulator +# before the call. Both functions expect DINDEX to contain the destination +# address and SINDEX to contain the source address. +bcopy_7: mov DINDIR,SINDIR mov DINDIR,SINDIR - mov DINDIR,SINDIR ret - +bcopy_5: + mov DINDIR,SINDIR bcopy_4: mov DINDIR,SINDIR +bcopy_3: mov DINDIR,SINDIR mov DINDIR,SINDIR mov DINDIR,SINDIR ret -bcopy_3_dfdat: +bcopy_7_dfdat: mov DINDIR,DFDAT mov DINDIR,DFDAT - mov DINDIR,DFDAT ret - +bcopy_5_dfdat: + mov DINDIR,DFDAT bcopy_4_dfdat: mov DINDIR,DFDAT +bcopy_3_dfdat: mov DINDIR,DFDAT mov DINDIR,DFDAT mov DINDIR,DFDAT ret @@ -962,7 +992,6 @@ dma: mov DFCNTRL,SINDEX dma1: -dma2: test SSTAT0,0x1 jnz dma3 # DMADONE test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun @@ -978,19 +1007,14 @@ dma4: test DFSTATUS,0x1 jz dma4 # !FIFOEMP -# Now shut the DMA enables off, and copy STCNT (ie. the underrun -# amount, if any) to the SCB registers; SG_COUNT will get copied to -# the SCB's residual S/G count field after sg_advance is called. Make -# sure that the DMA enables are actually off first lest we get an ILLSADDR. +# Now shut the DMA enables off and make sure that the DMA enables are +# actually off first lest we get an ILLSADDR. # dma5: clr DFCNTRL # disable DMA dma6: test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK - mvi DINDEX,SCBARRAY+15 - mvi STCNT call bcopy_3 - ret dma_finish: @@ -1022,10 +1046,9 @@ mvi SXFRCTL0,0x8a # DFON|SPIOEN|CLRCHN -# Initialize scatter-gather pointers by setting up the working copy -# in scratch RAM. -# - call sg_scb2ram +# Make sure that the system knows we have not been through a DATA +# phase. + and FLAGS, 0xfb # !DPHASE # Initialize SCSIRATE with the appropriate value for this target. # @@ -1041,29 +1064,6 @@ mvi INTSTAT,NO_IDENT ret # no - cause a kernel panic -# Find out if disconnection is ok from the information the BIOS has left -# us. The tcl from SCBARRAY+1 should be in SINDEX; A will -# contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok) -# on exit. -# -# To allow for wide or twin busses, we check the upper bit of the target ID -# and the channel ID and look at the appropriate disconnect register. -# -disconnect: - and FUNCTION1,0x70,SINDEX # strip off extra just in case - mov A,FUNCTION1 - test SINDEX, 0x88 jz disconnect_a - - test DISC_DSB_B,A jz disconnect1 # bit nonzero if DISabled - clr A ret - -disconnect_a: - test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled - clr A ret - -disconnect1: - mvi A,0x40 ret - # Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch # the SCB to it. Have the kernel print a warning message if it can't be # found, and generate an ABORT message to the target. SINDEX should be @@ -1073,7 +1073,7 @@ mov A,SAVED_TCL mov SCBPTR,SINDEX # switch to new SCB cmp SCBARRAY+1,A jne findSCB1 # target ID/channel/lun match? - test SCBARRAY+0,0x4 jz findSCB1 # should be disconnected + test SCBARRAY+0,DISCONNECTED jz findSCB1 # should be disconnected test SCBARRAY+0,TAG_ENB jnz get_tag ret @@ -1089,113 +1089,40 @@ call scsisig ret -# Make a working copy of the scatter-gather parameters in the SCB. +# Make a working copy of the scatter-gather parameters from the SCB. # sg_scb2ram: + mvi DINDEX,HADDR + mvi SCBARRAY+19 call bcopy_7 + + mvi DINDEX,STCNT + mvi SCBARRAY+23 call bcopy_3 + mov SG_COUNT,SCBARRAY+2 mvi DINDEX,SG_NEXT mvi SCBARRAY+3 call bcopy_4 + ret - mvi SG_NOLOAD,0x80 - test SCBARRAY+0,0x10 jnz return # don't reload s/g? - clr SG_NOLOAD ret - -# Copying RAM values back to SCB, for Save Data Pointers message. +# Copying RAM values back to SCB, for Save Data Pointers message, but +# only if we've actually been into a data phase to change them. This +# protects against bogus data in scratch ram and the residual counts +# since they are only initialized when we go into data_in or data_out. # sg_ram2scb: + test FLAGS, DPHASE jz return mov SCBARRAY+2,SG_COUNT mvi DINDEX,SCBARRAY+3 mvi SG_NEXT call bcopy_4 + + mvi DINDEX,SCBARRAY+19 + mvi SHADDR call bcopy_4 - and SCBARRAY+0,0xef,SCBARRAY+0 - test SG_NOLOAD,0x80 jz return # reload s/g? - or SCBARRAY+0,SG_LOAD ret - -# Load a struct scatter if needed and set up the data address and -# length. If the working value of the SG count is nonzero, then -# we need to load a new set of values. -# -# This, like the above DMA, assumes a little-endian host data storage. -# -sg_load: - test SG_COUNT,0xff jz return # SG being used? - test SG_NOLOAD,0x80 jnz return # don't reload s/g? - - clr HCNT+2 - clr HCNT+1 - mvi HCNT+0,SG_SIZEOF - - mvi DINDEX,HADDR - mvi SG_NEXT call bcopy_4 - - mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET - -# Wait for DMA from host memory to data FIFO to complete, then disable -# DMA and wait for it to acknowledge that it's off. -# - - call dma_finish - -# Copy data from FIFO into SCB data pointer and data count. This assumes -# that the struct scatterlist has this structure (this and sizeof(struct -# scatterlist) == 12 are asserted in aic7xxx.c): -# -# struct scatterlist { -# char *address; /* four bytes, little-endian order */ -# ... /* four bytes, ignored */ -# unsigned short length; /* two bytes, little-endian order */ -# } -# - -# Not in FreeBSD. the scatter list entry is only 8 bytes. -# -# struct ahc_dma_seg { -# physaddr addr; /* four bytes, little-endian order */ -# long len; /* four bytes, little endian order */ -# }; -# - - mvi DINDEX, SCBARRAY+19 - call bcopy_4_dfdat - -# For Linux, we must throw away four bytes since there is a 32bit gap -# in the middle of a struct scatterlist - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT - - call bcopy_3_dfdat #Only support 24 bit length. +# Use the residual number since STCNT is corrupted by any message transfer + mvi SCBARRAY+15 call bcopy_3 ret -# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled, -# and the SCSI transfer count is zero (note that this should be called -# right after a DMA finishes), then move the working copies of the SG -# pointer/length along. If the SCSI transfer count is not zero, then -# presumably the target is disconnecting - do not reload the SG values -# next time. -# -sg_advance: - test SG_COUNT,0xff jz return # s/g enabled? - - test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero? - test STCNT+1,0xff jnz sg_advance1 - test STCNT+2,0xff jnz sg_advance1 - - clr SG_NOLOAD # reload s/g next time - dec SG_COUNT # one less segment to go - - clr A # add sizeof(struct scatter) - add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0 - adc SG_NEXT+1,A,SG_NEXT+1 - adc SG_NEXT+2,A,SG_NEXT+2 - adc SG_NEXT+3,A,SG_NEXT+3 ret - -sg_advance1: - mvi SG_NOLOAD,0x80 ret # don't reload s/g next time - # Add the array base SYNCNEG to the target offset (the target address # is in SCSIID), and return the result in SINDEX. The accumulator # contains the 3->8 decoding of the target ID on return. @@ -1217,7 +1144,7 @@ # reject, you wouldn't be able to tell which message was the culpret. # mk_dtr: - test SCBARRAY+0,0xc0 jz return # NEEDWDTR|NEEDSDTR + test SCBARRAY+0,0x90 jz return # NEEDWDTR|NEEDSDTR test SCBARRAY+0,NEEDWDTR jnz mk_wdtr_16bit or FLAGS, MAX_OFFSET # Force an offset of 15 or 8 if WIDE diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx_asm.c linux/drivers/scsi/aic7xxx_asm.c --- v1.3.35/linux/drivers/scsi/aic7xxx_asm.c Wed Aug 2 13:21:07 1995 +++ linux/drivers/scsi/aic7xxx_asm.c Mon Oct 23 13:21:53 1995 @@ -27,7 +27,7 @@ * A