diff --context --recursive --new-file linux-orig/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex *** linux-orig/Documentation/cdrom/cdrom-standard.tex Thu Jan 1 01:00:00 1970 --- linux/Documentation/cdrom/cdrom-standard.tex Wed Apr 17 22:46:35 1996 *************** *** 0 **** --- 1,837 ---- + \documentclass{article} + \def\version{$Id: cdrom-standard.tex,v 0.4 1996/04/17 20:46:34 david Exp $} + + \evensidemargin=0pt + \oddsidemargin=0pt + \topmargin=-\headheight \advance\topmargin by -\headsep + \textwidth=15.99cm \textheight=24.62cm % normal A4, 1'' margin + + \def\linux{{\sc Linux}} + \def\cdrom{{\sc CDrom}} + \def\cdromc{{\tt cdrom.c}} + \def\ucdrom{{\tt ucdrom.h}} + + \everymath{\it} \everydisplay{\it} + \catcode `\_=\active \def_{\_\penalty100 } + \catcode`\<=\active \def<#1>{{\langle\hbox{\rm#1}\rangle}} + + \begin{document} + \title{A \linux\ \cdrom\ standard} + \author{David van Leeuwen\\{\normalsize\tt david@tm.tno.nl}} + + \maketitle + + \section{Introduction} + + \linux\ is probably the Unix-like operating system that supports the widest + variety of hardware devices. The reasons for this are presumably + \begin{itemize} + \item The large list of different hardware devices available for the popular + IBM PC-architecture, + \item The open design of the operating system, such that everybody can + write a driver for Linux. + \end{itemize} + The vast choice and openness has lead not only to a wide support of + hardware devices, but also to a certain divergence in + behavior. Especially for \cdrom\ devices, the way a particular drive + reacts to a `standard' $ioctl()$ call varies a lot from one brand + to another. + + Undoubtedly, this has a reason. Since the beginning of the \cdrom, + many different interfaces developed. Most of them had proprietary + interfaces, which means that a separate driver had to be written for + each new type of interface. Nowadays, all new \cdrom\ types are either + ATAPI/IDE or SCSI. But history has delivered us \cdrom\ support for + some 10 or so different interfaces. Not all drives have the same + capabilities, and all different interfaces use different i/o formats + for the data. For the interfacing with the \linux\ operating system + and software, this has lead to a rather wild set of commands and data + formats. Presumably, every \cdrom\ device drive author has added his + own set of ioctl commands and used a format reminiscent of the + underlying hardware. Any structure is lost. + + Apart from the somewhat unstructured interfacing with software, the + actual execution of the commands is different for most of the + different drivers: e.g., some drivers close the tray if an $open()$ call + occurs while the tray is unloaded, others not. Some drivers lock the + door upon opening the device, to prevent an incoherent file system, + but others don't, to allow software ejection. Undoubtedly, the + capabilities of the different drives vary, but even when two drives have + the same capability the driver behavior may be different. + + Personally, I think that the most important drive interfaces will be + the IDE/ATAPI drives and of course the SCSI drives, but as prices of + hardware drop continuously, it is not unlikely that people will have + more than one \cdrom\ drive, possibly of mixed types. (In December + 1994, one of the cheapest \cdrom\ drives was a Philips cm206, a + double-speed proprietary drive. In the months that I was busy writing + a \linux\ driver for it, proprietary drives became old-fashioned and + IDE/ATAPI drives became standard. At the time of writing (April 1996) + the cheapest double speed drive is IDE and at one fifth of the price + of its predecessor. Eight speed drives are available now.) + + This document defines (in pre-release versions: proposes) the various + $ioctl$s, and the way the drivers should implement this. + + \section{Standardizing through another software level} + \label{cdrom.c} + + At the time this document is written, all drivers directly implement + the $ioctl()$ calls through their own routines, with the danger of + forgetting calls to $verify_area()$ and the risk of divergence in + implementation. + + For this reason, we\footnote{The writing style is such that `we' is + used when (at least part of) the \cdrom-device driver authors support + the idea, an `I' is used for personal opinions} propose to define + another software-level, that separates the $ioctl()$ and $open()$ + implementation from the actual hardware implementation. We believe + that \cdrom\ drives are specific enough (i.e., different from other + block-devices such as floppy or hard disc drives), to define a set of + {\em \cdrom\ device operations}, $_dops$. These are of a + different nature than the classical block-device file operations + $_fops$. + + The extra interfacing level routines are implemented in a file + \cdromc, and a low-level \cdrom\ driver hands over the interfacing to + the kernel by registering the following general $struct\ file_operations$: + $$ + \halign{$#$\ \hfil&$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr + struct& file_operations\ cdrom_fops = \{\hidewidth\cr + &NULL, & lseek \cr + &block_read, & read - general\ block-dev\ read \cr + &block_write, & write - general block-dev write \cr + &NULL, & readdir \cr + &NULL, & select \cr + &cdrom_ioctl, & ioctl \cr + &NULL, & mmap \cr + &cdrom_open, & open \cr + &cdrom_release, & release \cr + &NULL, & fsync \cr + &NULL, & fasync \cr + &cdrom_media_changed, & media_change \cr + &NULL & revalidate \cr + \};\cr + } + $$ + Every active \cdrom\ device shares this $struct$. The routines declared + above are all implemented in \cdromc, and this is the place where the + {\em behavior\/} of all \cdrom-devices is defined, and hence + standardized. The implementation of the interfacing to the various + types of hardware still is done by the various \cdrom-device drivers, + but these routines only implement certain {\em capabilities\/} that + are typical to \cdrom\ (removable-media) devices. + + Registration of the \cdrom\ device driver should now be to the general + routines in \cdromc, not to the VFS any more. This is done though the + call + $$register_cdrom(int\ major, char * name, + struct\ cdrom_device_ops\ device_options) + $$ + + The device operations structure lists the implemented routines for + interfacing to the hardware, and some specifications of capabilities + of the device, such as the maximum head-transfer rate. [It is + impossible to come up with a complete list of all capabilities of + (future) \cdrom\ drives, as the developments in technology follow-up + at an incredible rate. Maybe write-operation (WORM devices) will + become very popular in the future.] The list now is: + $$ + \halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}& + $/*$ \rm# $*/$\hfil\cr + struct& cdrom_device_ops\ \{ \hidewidth\cr + &int& (* open)(dev_t, int)\cr + &void& (* release)(dev_t);\cr + &int& (* open_files)(dev_t); \cr + &int& (* drive_status)(dev_t);\cr + &int& (* disc_status)(dev_t);\cr + &int& (* media_changed)(dev_t);\cr + &int& (* tray_move)(dev_t, int);\cr + &int& (* lock_door)(dev_t, int);\cr + &int& (* select_speed)(dev_t, int);\cr + &int& (* select_disc)(dev_t, int);\cr + &int& (* get_last_session) (dev_t, struct\ cdrom_multisession *{});\cr + &int& (* get_mcn)(dev_t, struct\ cdrom_mcn *{});\cr + &int& (* reset)(dev_t);\cr + &int& (* audio_ioctl)(dev_t, unsigned\ int, void *{});\cr + &int& (* dev_ioctl)(dev_t, unsigned\ int, unsigned\ long);\cr + \noalign{\medskip} + &\llap{const\ }int& capability;& capability flags \cr + &int& mask;& mask of capability: disables them \cr + &\llap{$const\ $}float& speed;& maximum speed for reading data \cr + &\llap{$const\ $}int& minors;& number of supported minor devices \cr + &\llap{$const\ $}int& capacity;& number of discs in jukebox \cr + \noalign{\medskip} + &int& options;& options flags \cr + &long& mc_flags;& media-change buffer flags ($2\times16$) \cr + \}\cr + } + $$ The \cdrom-driver should simply implement (some of) these + functions, and register the functions to the global \cdrom\ driver, + which performs interfacing with the Virtual File System and system + $ioctl$s. The flags $capability$ specify the hardware-capabilities on + registration of the device, the flags $mask$ can be used to mask some + of those capabilities (for one reason or another). The value $minors$ + should be a positive value indicating the number of minor devices that + are supported by the driver, normally~1. (They are supposed to be + numbered from 0 upwards). The value $capacity$ should be the number of + discs the drive can hold simultaneously, if it is designed as a + juke-box, or otherwise~1. + + Two registers contain variables local to the \cdrom\ device. The flags + $options$ are used to specify how the general \cdrom\ routines + should behave. These various flags registers should provide enough + flexibility to adapt to the different user's wishes (and {\em not\/} + the `arbitrary' wishes of the author of the low-level device driver, + as is the case in the old scheme). The register $mc_flags$ is used to + buffer the information from $media_changed()$ to two separate queues. + + Note that most functions have fewer parameters than their + $blkdev_fops$ counterparts. This is because very little of the + information in the structures $inode$ and $file$ are used, the main + parameter is the device $dev$, from which the minor-number can be + extracted. (Most low-level \cdrom\ drivers don't even look at that value + as only one device is supported.) + + The intermediate software layer that \cdromc\ forms will performs some + additional bookkeeping. The minor number of the device is checked + against the maximum registered in $_dops$. The function + $cdrom_ioctl()$ will verify the appropriate user-memory regions for + read and write, and in case a location on the CD is transferred, it + will `sanitize' the format by making requests to the low-level drivers + in a standard format, and translating all formats between the + user-software and low level drivers. This relieves much of the drivers + memory checking and format checking and translation. Also, the + necessary structures will be declared on the program stack. + + The implementation of the functions should be as defined in the + following sections. Three functions {\em must\/} be implemented, + namely $open()$, $release()$ and $open_files()$. Other functions may + be omitted, their corresponding capability flags will be cleared upon + registration. Generally, a function returns zero on success and + negative on error. A function call should return only after the + command has completed, but of course waiting for the device should not + use processor time. + + \subsection{$Open(dev_t\ dev, int\ purpose)$} + + $Open()$ should try to open the device for a specific $purpose$, which + can be either: + \begin{itemize} + \item[0] Open for data read, as is used by {\tt mount()} (2), or the + user commands {\tt dd} or {\tt cat}. + \item[1] Open for $ioctl$ commanding, as is used for audio-CD playing + programs mostly. + \end{itemize} + In this routine, a static counter should be updated, reflecting the + number of times the specific device is successfully opened (and in + case the driver supports modules, the call $MOD_INC_USE_COUNT$ + should be performed exactly once, if successful). The return value is + negative on error, and zero on success. The open-for-ioctl call can + only fail if there is no hardware. + + Notice that any strategic code (closing tray upon $open()$, etc.)\ is + done by the calling routine in \cdromc, so the low-level routine + should only be concerned with proper initialization and device-use + count. + + \subsection{$Release(dev_t\ dev)$} + + The use-count of the device $dev$ should be decreased by 1, and a + single call $MOD_DEC_USE_COUNT$ should be coded here. Possibly other + device-specific actions should be taken such as spinning down the + device. However, strategic actions such as ejection of the tray, or + unlocking the door, should be left over to the general routine + $cdrom_release()$. Also, the invalidation of the allocated buffers in + the VFS is taken care of by the routine in \cdromc. + + \subsection{$Open_files(dev_t\ dev)$} + + This function should return the internal variable use-count of the + device $dev$. The use-count is not implemented in the routines in + \cdromc\ itself, because there may be many minor devices connected to + a single low-level driver. + + \subsection{$Drive_status(dev_t\ dev)$} + \label{drive status} + + The function $drive_status$, if implemented, should provide + information of the status of the drive (not the status of the disc, + which may or may not be in the drive). In \ucdrom\ the possibilities + are listed: + $$ + \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr + CDS_NO_INFO& no information available\cr + CDS_NO_DISC& no disc is inserted, tray is closed\cr + CDS_TRAY_OPEN& tray is opened\cr + CDS_DRIVE_NOT_READY& something is wrong, tray is moving?\cr + CDS_DISC_OK& a disc is loaded and everything is fine\cr + } + $$ + + \subsection{$Disc_status(dev_t\ dev)$} + \label{disc status} + + As a complement to $drive_status()$, this functions can provide the + general \cdrom-routines with information about the current disc that is + inserted in the drive represented by $dev$. The history of development + of the CD's use as a carrier medium for various digital information + has lead to many different disc types, hence this function can return: + $$ + \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr + CDS_NO_INFO& no information available\cr + CDS_NO_DISC& no disc is inserted, or tray is opened\cr + CDS_AUDIO& Audio disc (2352 audio bytes/frame)\cr + CDS_DATA_1& data disc, mode 1 (2048 user bytes/frame)\cr + CDS_DATA_2& data disc, mode 2 (2336 user bytes/frame)\cr + CDS_XA_2_1& mixed data (XA), mode 2, form 1 (2048 user bytes)\cr + CDS_XA_2_2& mixed data (XA), mode 2, form 1 (2324 user bytes)\cr + } + $$ + As far as I know, \cdrom's are always of type $CDS_DATA_1$. For + some information concerning frame layout of the various disc types, see + a recent version of {\tt cdrom.h}. + + \subsection{$Media_changed(dev\_t\ dev)$} + + This function is very similar to the original function in $struct\ + file_operations$. It returns 1 if the medium of the device $dev$ has + changed since the last call, and 0 otherwise. Note that by `re-routing' + this function through $cdrom_media_changed()$, we can implement + separate queues for the VFS and a new $ioctl()$ function that can + report device changes to software (e.g., an auto-mounting daemon). + + \subsection{$Tray_move(dev_t\ dev, int\ position)$} + + This function, if implemented, should control the tray movement. (No + other function should control this.) The parameter $position$ controls + the desired direction of movement: + \begin{itemize} + \item[0] Close tray + \item[1] Open tray + \end{itemize} + This function returns 0 upon success, and a non-zero value upon + error. Note that if the tray is already in the desired position, no + action need be taken, and the return value should be 0. + + \subsection{$Lock_door(dev_t\ dev, int\ lock)$} + + This function (and no other code) controls locking of the door, if the + drive allows this. The value of $lock$ controls the desired locking + state: + \begin{itemize} + \item[0] Unlock door, manual opening is allowed + \item[1] Lock door, tray cannot be ejected manually + \end{itemize} + Return values are as for $tray_move()$. + + \subsection{$Select_speed(dev_t\ dev, int\ speed)$} + + Although none of the drivers has implemented this function so far, + some drives are capable of head-speed selection, and hence this is a + capability that should be standardized through a function in the + device-operations structure. This function should select the speed at + which data is read or audio is played back. The special value `0' + means `auto-selection', i.e., maximum data-rate or real-time audio + rate. If the drive doesn't have this `auto-selection' capability, the + decision should be made on the current disc loaded and the return + value should be positive. A negative return value indicates an + error. (Although the audio-low-pass filters probably aren't designed + for it, more than real-time playback of audio might be used for + high-speed copying of audio tracks). Badly pressed \cdrom s may + benefit from less-than-maximum head rate. + + \subsection{$Select_disc(dev_t\ dev, int\ number)$} + + If the drive can store multiple discs (a juke-box), it is likely that + a disc selection can be made by software. This function should perform + disc selection. It should return the number of the selected disc on + success, a negative value on error. Currently, none of the \linux\ + \cdrom\ drivers appear to support such functionality, but it defined + here for future purpose. + + \subsection{$Get_last_session(dev_t\ dev, struct\ cdrom_multisession * + ms_info)$} + + This function should implement the old corresponding $ioctl()$. For + device $dev$, the start of the last session of the current disc should + be returned in the pointer argument $ms_info$. Note that routines in \cdromc\ have sanitized this argument: its + requested format will {\em always\/} be of the type $CDROM_LBA$ + (linear block addressing mode), whatever the calling software + requested. But sanitization goes even further: the low-level + implementation may return the requested information in $CDROM_MSF$ + format if it wishes so (setting the $ms_info\rightarrow addr_format$ + field appropriately, of course) and the routines in \cdromc\ will make + the transform if necessary. The return value is 0 upon success. + + \subsection{$Get_mcn(dev_t\ dev, struct\ cdrom_mcn * mcn)$} + + Some discs carry a `Media Catalog Number' (MCN), also called + `Universal Product Code' (UPC). This number should reflect the number that + is generally found in the bar-code on the product. Unfortunately, the + few discs that carry such a number on the disc don't even use the same + format. The return argument to this function is a pointer to a + pre-declared memory region of type $struct\ cdrom_mcn$. The MCN is + expected as a 13-character string, terminated by a null-character. + + \subsection{$Reset(dev_t dev)$} + + This call should implement hard-resetting the drive (although in + circumstances that a hard-reset is necessary, a drive may very well + not listen to commands anymore). Preferably, control is returned to the + caller only after the drive has finished resetting. + + \subsection{$Audio_ioctl(dev_t\ dev, unsigned\ int\ cmd, void * + arg)$} + + Some of the \cdrom-$ioctl$s defined in {\tt cdrom.h} can be + implemented by the routines described above, and hence the function + $cdrom_ioctl$ will use those. However, most $ioctl$s deal with + audio-control. We have decided to leave these accessed through a + single function, repeating the arguments $cmd$ and $arg$. Note that + the latter is of type $void*{}$, rather than $unsigned\ long\ + int$. The routine $cdrom_ioctl()$ does do some useful things, + though. It sanitizes the address format type to $CDROM_MSF$ (Minutes, + Seconds, Frames) for all audio calls. It also verifies the memory + location of $arg$, and reserves stack-memory for the argument. This + makes implementation of the $audio_ioctl()$ much simpler than in the + old driver scheme. For an example you may look up the function + $cm206_audio_ioctl()$ in {\tt cm206.c} that should be updated with + this documentation. + + An unimplemented ioctl should return $-EINVAL$, but a harmless request + (e.g., $CDROMSTART$) may be ignored by returning 0 (success). Other + errors should be according to the standards, whatever they are. (We + may decide to sanitize the return value in $cdrom_ioctl()$, in order + to guarantee a uniform interface to the audio-player software.) + + \subsection{$Dev_ioctl(dev_t\ dev, unsigned\ int\ cmd, unsigned\ long\ + arg)$} + + Some $ioctl$s seem to be specific to certain \cdrom\ drives. That is, + they are introduced to service some capabilities of certain drives. In + fact, there are 6 different $ioctl$s for reading data, either in some + particular kind of format, or audio data. Not many drives support + reading audio tracks as data, I believe this is because of protection + of copyrights of artists. Moreover, I think that if audio-tracks are + supported, it should be done through the VFS and not via $ioctl$s. A + problem here could be the fact that audio-frames are 2352 bytes long, + so either the audio-file-system should ask for 75264 bytes at once + (the least common multiple of 512 and 2352), or the drivers should + bend their backs to cope with this incoherence (to which I would + oppose, this code then should be standardized in \cdromc). + + Because there are so many $ioctl$s that seem to be introduced to + satisfy certain drivers,\footnote{Is there software around that actually uses + these? I 'd be interested!} any `non-standard' $ioctl$s are routed through + the call $dev_ioctl()$. In principle, `private' $ioctl$s should be + numbered after the device's major number, and not the general \cdrom\ + $ioctl$ number, {\tt 0x53}. Currently the non-supported $ioctl$s are: + {\it CDROMREADMODE1, CDROMREADMODE2, CDROMREADAUDIO, CDROMREADRAW, + CDROMREADCOOKED, CDROMSEEK, CDROMPLAY\-BLK and CDROMREADALL}. + + \subsection{\cdrom\ capabilities} + + Instead of just implementing some $ioctl$ calls, the interface in + \cdromc\ supplies the possibility to indicate the {\em capabilities\/} + of a \cdrom\ drive. This can be done by ORing any number of + capability-constants that are defined in \ucdrom\ at the registration + phase. Currently, the capabilities are any of: + $$ + \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr + CDC_CLOSE_TRAY& can close tray by software control\cr + CDC_OPEN_TRAY& can open tray\cr + CDC_LOCK& can lock and unlock the door\cr + CDC_SELECT_SPEED& can select speed, in units of $\sim$150\,kB/s\cr + CDC_SELECT_DISC& drive is juke-box\cr + CDC_MULTI_SESSION& can read sessions $>\rm1$\cr + CDC_MCN& can read Medium Catalog Number\cr + CDC_MEDIA_CHANGED& can report if disc has changed\cr + CDC_PLAY_AUDIO& can perform audio-functions (play, pause, etc)\cr + } + $$ + The capability flag is declared $const$, to prevent drivers to + accidentally tamper with the contents. However, upon registration, + some (claimed) capability flags may be cleared if the supporting + function has not been implemented (see $register_cdrom()$ in + \cdromc). + + If you want to disable any of the capabilities, there is a special + flag register $_dops.mask$ that may (temporarily) disable + certain capabilities. In the file \cdromc\ you will encounter many + constructions of the type + $$\it + if\ (cdo\rightarrow capability \mathrel\& \mathord{\sim} cdo\rightarrow mask + \mathrel{\&} CDC_) \ldots + $$ + The $mask$ could be set in the low-level driver code do disable + certain capabilities for special brands of the device that can't + perform the actions. However, there is not (yet) and $ioctl$ to set + the mask\dots The reason is that I think it is better to control the + {\em behavior\/} rather than the {\em capabilities}. + + \subsection{Options} + + A final flag register controls the {\em behavior\/} of the \cdrom\ + drives, in order to satisfy the different users's wishes, hopefully + independently of the ideas of the respectable author that happened to + have made the drive's support available to the \linux\ community. The + current behavior options are: + $$ + \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr + CDO_AUTO_CLOSE& try to close tray upon device $open()$\cr + CDO_AUTO_EJECT& try to open tray on last device $close()$\cr + CDO_USE_FFLAGS& use $file_pointer\rightarrow f_flags$ to indicate + purpose for $open()$\cr + CDO_LOCK& try to lock door if device is opened\cr + CDO_CHECK_TYPE& ensure disc type is data if opened for data\cr + } + $$ + + The initial value of this register is $CDO_AUTO_CLOSE \mathrel| + CDO_USE_FFLAGS \mathrel| CDO_LOCK$, reflecting my own view on user + interface and software standards. Before you protest, there are two + new $ioctl$s implemented in \cdromc, that allow you to control the + behavior by software. These are: + $$ + \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr + CDROM_SET_OPTIONS& set options specified in $(int)\ arg$\cr + CDROM_CLEAR_OPTIONS& clear options specified in $(int)\ arg$\cr + } + $$ + One option needs some more explanation: $CDO_USE_FFLAGS$. In the next + section we explain what the need for this option is. + + \section{The need to know the purpose of opening} + + Traditionally, Unix devices can be used in two different `modes', + either by reading/writing to the device file, or by issuing + controlling commands to the device, by the device's $ioctl()$ + call. The problem with \cdrom\ drives, is that they can be used for + two entirely different purposes. One is to mount removable + file systems, \cdrom s, the other is to play audio CD's. Audio commands + are implemented entirely through $ioctl$s, presumably because the + first implementation (SUN?) has been such. In principle there is + nothing wrong with this, but a good control of the `CD player' demands + that the device can {\em always\/} be opened in order to give the + $ioctl$ commands, regardless of the state the drive is in. + + On the other hand, when used as a removable-media disc drive (what the + original purpose of \cdrom s is) we would like to make sure that the + disc drive is ready for operation upon opening the device. In the old + scheme, some \cdrom\ drivers don't do any integrity checking, resulting + in a number of i/o errors reported by the VFS to the kernel when an + attempt for mounting a \cdrom\ on an empty drive occurs. This is not a + particularly elegant way to find out that there is no \cdrom\ inserted; + it more-or-less looks like the old IBM-PC trying to read an empty floppy + drive for a couple of seconds, after which the system complains it + can't read from it. Nowadays we can {\em sense\/} the existence of a + removable medium in a drive, and we believe we should exploit that + fact. An integrity check on opening of the device, that verifies the + availability of a \cdrom\ and its correct type (data), would be + desirable. + + These two ways of using a \cdrom\ drive, principally for data and + secondarily for playing audio discs, have different demands for the + behavior of the $open()$ call. Audio use simply wants to open the + device in order to get a file handle which is needed for issuing + $ioctl$ commands, while data use wants to open for correct and + reliable data transfer. The only way user programs can indicate what + their {\em purpose\/} of opening the device is, is trough the $flags$ + parameter (see {\tt open(2)}). For \cdrom\ devices, these flags aren't + implemented (some drivers implement checking for write-related flags, + but this is not strictly necessary if the device file has correct + permission flags). Most option flags simply don't make sense to + \cdrom\ devices: $O_CREAT$, $O_NOCTTY$, $O_TRUNC$, $O_APPEND$, and + $O_SYNC$ have no meaning to a \cdrom. + + We therefore propose to use the flag $O_NONBLOCK$ as an indication + that the device is opened just for issuing $ioctl$ + commands. Strictly, the meaning of $O_NONBLOCK$ is that opening and + subsequent calls to the device don't cause the calling process to + wait. We could interpret this as ``don't wait until someone has been + inserted some valid data-\cdrom.'' Thus, our proposal of the + implementation for the $open()$ call for \cdrom s is: + \begin{itemize} + \item If no other flags are set than $O_RDONLY$, the device is opened + for data transfer, and the return value will be 0 only upon successful + initialization of the transfer. The call may even induce some actions + on the \cdrom, such as closing the tray. + \item If the option flag $O_NONBLOCK$ is set, opening will always be + successful, unless the whole device doesn't exist. The drive will take + no actions whatsoever. + \end{itemize} + + \subsection{And what about standards?} + + You might hesitate to accept this proposal as is comes from the + \linux\ community, and not from some standardizing institute. What + about SUN, SGI, HP and all those other Unix and hardware vendors? + Well, these companies are in the lucky position that they generally + control both the hardware and software of their supported products, + and are large enough to set their own standard. They do not have to + deal with a dozen or more different, competing hardware + configurations.\footnote{Personally, I think that SUN's approach to + mounting \cdrom s is very good in origin: under Solaris a + volume-daemon automatically mounts a newly inserted \cdrom\ under {\tt + /cdrom/$$/}. In my opinion they should have pushed this + further and have {\em every\/} \cdrom\ on the local area network be + mounted at the similar location, i.e., no matter in which particular + machine you insert a \cdrom, it will always appear at the same + position in the directory tree, on every system. When I wanted to + implement such a user-program for \linux, I came across the + differences in behavior of the various drivers, and the need for an + $ioctl$ informing about media changes.} + + We believe that using $O_NONBLOCK$ as indication for opening a device + for $ioctl$ commanding only, can be easily introduced in the \linux\ + community. All the CD-player authors will have to be informed, we can + even send in our own patches to the programs. The use of $O_NONBLOCK$ + has most likely no influence on the behavior of the CD-players on + other operating systems than \linux. Finally, a user can always revert + to old behavior by a call to $ioctl(file_descriptor, CDROM_CLEAR_OPTIONS, + CDO_USE_FFLAGS)$. + + \subsection{The preferred strategy of $open()$} + + The routines in \cdromc\ are designed in such way, that a run-time + configuration of the behavior of \cdrom\ devices (of {\em any\/} type) + can be carried out, by the $CDROM_SET/CLEAR_OPTIONS$ $ioctls$. Thus, various + modes of operation can be set: + \begin{description} + \item[$CDO_AUTO_CLOSE \mathrel| CDO_USE_FFLAGS \mathrel| CDO_LOCK$] + This is the default setting. (With $CDO_CHECK_TYPE$ it will be better, + in the future.) If the device is not yet opened by any other process, + and it is opened for data ($O_NONBLOCK$ is not set) and the tray is + found open, an attempt to close the tray is made. Then, it is verified + that a disc is in the drive and, if $CDO_CHECK_TYPE$ is set, that its + type is `data mode 1.' Only if all tests are passed, the return value + is zero. The door is locked to prevent file system corruption. If + opened for audio ($O_NONBLOCK$ is set), no actions are taken and a + value of 0 will be returned. + \item[0] $Open()$ will always be successful, the option flags are + ignored. Neither actions are undertaken, nor any integrity checks are + made. + \item[$CDO_AUTO_CLOSE \mathrel| CDO_AUTO_EJECT \mathrel| CDO_LOCK$] + This mimics the behavior of the current sbpcd-driver. The option flags + are ignored, the tray is closed on the first open, if + necessary. Similarly, the tray is opened on the last release, i.e., if + a \cdrom\ is unmounted, it is automatically ejected, such that the + user can replace it. + \end{description} + We hope that these option can convince everybody (both driver + maintainers and user program developers) to adapt to the new cdrom + driver scheme and option flag interpretation. + + \section{Description of routines in \cdromc} + + Only a few routines in \cdromc\ are exported to the drivers. In this + section we will treat these, as well as the functioning of the routines + that `take over' the interface to the kernel. The header file + belonging to \cdromc\ is called \ucdrom, but may be included in {\tt + cdrom.h} in the future. + + \subsection{$struct\ file_operations\ cdrom_fops$} + + The contents of this structure has been described in + section~\ref{cdrom.c}, and this structure should be used in + registering the block device to the kernel: + $$ + register_blkdev(major, , \&cdrom_fops); + $$ + + \subsection{$Int\ register_cdrom(int\ major, char * name, struct\ + cdrom_device_ops\ * cdo)$} + + Similar to registering $cdrom_fops$ to the kernel, the device + operations structure, as described in section~\ref{cdrom.c}, should be + registered to the general \cdrom\ interface: + $$ + register_cdrom(major, , \&_dops); + $$ + This function returns zero upon success, and non-zero upon failure. + + \subsection{$Int\ unregister_cdrom(int\ major, char * name)$} + + Unregistering device $name$ with major number $major$ disconnects the + registered device-operation routines from the \cdrom\ interface. + This function returns zero upon success, and non-zero upon failure. + + \subsection{$Int\ cdrom_open(struct\ inode * ip, struct\ file * fp)$} + + This function is not called directly by the low-level drivers, it is + listed in the standard $cdrom_fops$. If the VFS opens a file, this + function becomes active. A strategy logic is implemented in this + routine, taking care of all capabilities and options that are set in + the $cdrom_device_ops$ connected to the device. Then, the program flow is + transferred to the device_dependent $open()$ call. + + \subsection{$Void\ cdrom_release(struct\ inode *ip, struct\ file + *fp)$} + + This function implements the reverse-logic of $cdrom_open()$, and then + calls the device-dependent $release()$ routine. When the use-count + has reached 0, the allocated buffers in the are flushed by calls to + $sync_dev(dev)$ and $invalidate_buffers(dev)$. + + + \subsection{$Int\ cdrom_ioctl(struct\ inode *ip, struct\ file *fp, + unsigned\ int\ cmd, unsigned\ long\ arg)$} + \label{cdrom-ioctl} + + This function handles all $ioctl$ requests for \cdrom\ devices in a + uniform way. The different calls fall into three categories: $ioctl$s + that can be directly implemented by device operations, ones that are + routed through the call $audio_ioctl()$, and the remaining ones, that + are presumable device-dependent. Generally, a negative return value + indicates an error. + + \subsubsection{Directly implemented $ioctl$s} + \label{ioctl-direct} + + The following `old' \cdrom-$ioctl$s are implemented by directly + calling device-operations in $cdrom_device_ops$, if implemented and + not masked: + \begin{description} + \item[CDROMMULTISESSION] Requests the last session on a \cdrom. + \item[CDROMEJECT] Open tray. + \item[CDROMCLOSETRAY] Close tray. + \item[CDROMEJECT_SW] If $arg\not=0$, set behavior to auto-close (close + tray on first open) and auto-eject (eject on last release), otherwise + set behavior to non-moving on $open()$ and $release()$ calls. + \item[CDROM_GET_MCN or CDROM_GET_UPC] Get the Medium Catalog Number from a CD. + \end{description} + + \subsubsection{$Ioctl$s rooted through $audio_ioctl()$} + \label{ioctl-audio} + + The following set of $ioctl$s are all implemented through a call to + the $cdrom_fops$ function $audio_ioctl()$. Memory checks and + allocation are performed in $cdrom_ioctl()$, and also sanitization of + address format ($CDROM_LBA$/$CDROM_MSF$) is done. + \begin{description} + \item[CDROMSUBCHNL] Get sub-channel data in argument $arg$ of type $struct\ + cdrom_subchnl *{}$. + \item[CDROMREADTOCHDR] Read Table of Contents header, in $arg$ of type + $struct\ cdrom_tochdr *{}$. + \item[CDROMREADTOCENTRY] Read a Table of Contents entry in $arg$ and + specified by $arg$ of type $struct\ cdrom_tocentry *{}$. + \item[CDROMPLAYMSF] Play audio fragment specified in Minute, Second, + Frame format, delimited by $arg$ of type $struct\ cdrom_msf *{}$. + \item[CDROMPLAYTRKIND] Play audio fragment in track-index format + delimited by $arg$ of type $struct cdrom_ti *{}$. + \item[CDROMVOLCTRL] Set volume specified by $arg$ of type $struct\ + cdrom_volctrl *{}$. + \item[CDROMVOLREAD] Read volume into by $arg$ of type $struct\ + cdrom_volctrl *{}$. + \item[CDROMSTART] Spin up disc. + \item[CDROMSTOP] Stop playback of audio fragment. + \item[CDROMPAUSE] Pause playback of audio fragment. + \item[CDROMRESUME] Resume playing. + \end{description} + + \subsubsection{New $ioctl$s in \cdromc} + + The following $ioctl$s have been introduced to allow user programs to + control the behavior of individual \cdrom\ devices. New $ioctl$ + commands an be identified by their underscores in the name. + \begin{description} + \item[CDROM_SET_OPTIONS] Set options specified by $arg$. Returns the + option flag register after modification. Use $arg = \rm0$ for reading + the current flags. + \item[CDROM_CLEAR_OPTIONS] Clear options specified by $arg$. Returns + the option flag register after modification. + \item[CDROM_SELECT_SPEED] Select head-rate speed of disc specified as + by $arg$. The value 0 means `auto-select', i.e., play audio discs at + real time and data disc at maximum speed. The value $arg$ is + checked against the maximum head rate of the drive found in + the $cdrom_dops$. + \item[CDROM_SELECT_DISC] Select disc numbered $arg$ from a juke-box. + First disc is numbered 0. The number $arg$ is checked against the + maximum number of discs in the juke-box found in the $cdrom_dops$. + \item[CDROM_MEDIA_CHANGED] Returns 1 if a disc has been changed since + the last call. Note that calls to cdrom_$media_changed$ by the VFS + are treated by an independent queue, so both mechanisms will detect + a media change once. Currently, \cdromc\ implements maximum 16 minors + per major device. + \item[CDROM_DRIVE_STATUS] Returns the status of the drive by a call to + $drive_status()$. Return values are as defined in section~\ref{drive + status}. Note that this call doesn't return information on the + current playing activity of the drive, this can be polled through an + $ioctl$ call to $CDROMSUBCHNL$. + \item[CDROM_DISC_STATUS] Returns the type of the disc currently in the + drive by a call to $disc_status()$. Return values are as defined in + section~\ref{disc status}. + \end{description} + + \subsubsection{Device dependent $ioct$s} + + Finally, all other $ioctl$s are passed to the function $dev_ioctl()$, + if implemented. No memory allocation or verification is carried out. + + \subsection{How to update your driver} + + We hope all \cdrom\ driver maintainers will understand the advantages + of re-routing the interface to the kernel though the new routines in + \cdromc. The following scheme should help you to update your + driver. It should not be too much work. We hope you want to take these + steps, in order to make the \linux\ \cdrom\ support more uniform and + more flexible. + \begin{enumerate} + \item Make a backup of your current driver. + \item Get hold of the files \cdromc\ and \ucdrom, they should be in + the directory tree that came with this documentation. + \item Include {\tt \char`\} just after {\tt cdrom.h}. + \item change the 3rd argument of $register_blkdev$ from + $\&_fops$ to $\&cdrom_fops$. + \item Just after that line, add a line to register to the \cdrom\ + routines: + $$register_cdrom(major, , _dops);$$ + Similarly, add a call to $unregister_cdrom()$. + \item Copy an example of the device-operations $struct$ to your source, + e.g., from {\tt cm206.c} $cm206_dops$, and change all entries to names + corresponding to your driver, or names you just happen to like. If + your driver doesn't support a certain function, make the entry + $NULL$. At the entry $capability$ you should list all capabilities + your drive could support, in principle. If your drive has a capability + that is not listed, please send me a message. + \item Implement all functions in your $_dops$ structure, + according to prototypes listed in \ucdrom, and specifications given in + section~\ref{cdrom.c}. Most likely you have already implemented + the code in a large part, and you may just have to adapt the prototype + and return values. + \item Rename your $_ioctl()$ function to $audio_ioctl$ and + change the prototype a little. Remove entries listed in the first part + in section~\ref{cdrom-ioctl}, if your code was OK, this are just calls + to the routines you adapted in the previous step. + \item You may remove all remaining memory checking code in the + $audio_ioctl()$ function that deal with audio commands (these are + listed in the second part of section~\ref{cdrom-ioctl}). There is no + need for memory allocation either, so most $case$s in the $switch$ + statement look similar to: + $$ + case\ CDROMREADTOCENTRY\colon + get_toc_entry\bigl((struct\ cdrom_tocentry *{})\ arg\bigr); + $$ + \item All remaining $ioctl$ cases must be moved to a separate + function, $_ioctl$, the device-dependent $ioctl$s. Note that + memory checking and allocation must be kept in this code! + \item Change the prototypes of $_open()$ and + $_release()$, and remove any strategic code (i.e., tray + movement, door locking, etc.). + \item Try to recompile the drivers. We advice you to use modules, both + for {\tt cdrom.o} and your driver, as debugging is much easier this + way. + \end{enumerate} + + \section{Thanks} + + Thanks to all the people involved. Thanks to Thomas Quinot, Jon Tombs, + Ken Pizzini, Eberhard M\"onkeberg and Andrew Kroll, the \linux\ + \cdrom\ device driver developers that were kind enough to give + sugestions and criticisms during the writing. Finally of course, I + want to thank Linus Torvalds for making this possible in the first + place. + + \end{document} + diff --context --recursive --new-file linux-orig/Documentation/cdrom/cm206 linux/Documentation/cdrom/cm206 *** linux-orig/Documentation/cdrom/cm206 Wed Apr 17 20:23:15 1996 --- linux/Documentation/cdrom/cm206 Wed Apr 17 20:38:25 1996 *************** *** 3,16 **** (c) 1995 David A. van Leeuwen ! Features as of version 0.33 --------------------------- - Full audio support, that is, both workman, workbone and cdp work now reasonably. Reading TOC still takes some time. xmcd has been reported to run successfully. - Made auto-probe code a little better, i hope ! Features as of version 0.28 --------------------------- - Full speed transfer rate (300 kB/s). - Minimum kernel memory usage for buffering (less than 3 kB). --- 3,22 ---- (c) 1995 David A. van Leeuwen ! Changes since version 0.99 ! -------------------------- ! - Interfacing to the kernel is routed though an extra interface layer, ! cdrom.c. This allows runtime-configurable `bahavior' of the cdrom-drive, ! independent of the driver. ! ! Features since version 0.33 --------------------------- - Full audio support, that is, both workman, workbone and cdp work now reasonably. Reading TOC still takes some time. xmcd has been reported to run successfully. - Made auto-probe code a little better, i hope ! Features since version 0.28 --------------------------- - Full speed transfer rate (300 kB/s). - Minimum kernel memory usage for buffering (less than 3 kB). *************** *** 21,32 **** - Auto-probing of adapter card's base port and irq line, also configurable at boot time or module load time. - Features still lacking - ---------------------- - - cm205ms+cm250 support. (I do have cm205ms docs now. I still have to - study Kai Petzke's cm205 drives to understand the workings of the - cm250 adapter card. Don't bet on me, write a driver yourself!) - Decide how you are going to use the driver. There are two options: --- 27,32 ---- *************** *** 66,74 **** ---------------------------- If you will only seldomly use the cd-rom driver, you can choose for option (b), install as a loadable module. You may have to re-compile ! the module when you upgrade the kernel to a new version. Read the file ! `README.modules' in /usr/src/linux. To install the module, you use the ! command, as root insmod /usr/src/linux/modules/cm206.o --- 66,81 ---- ---------------------------- If you will only seldomly use the cd-rom driver, you can choose for option (b), install as a loadable module. You may have to re-compile ! the module when you upgrade the kernel to a new version. ! ! Since version 0.96, much of the functionality has been transferred to ! a generic cdrom interface in the file cdrom.c. The module cm206.o ! depends on cdrom.o. If the latter is not compiled in in the kernel, ! you must explicitly load it before cm206.o: ! ! insmod /usr/src/linux/modules/cdrom.o ! ! To install the module, you use the command, as root insmod /usr/src/linux/modules/cm206.o *************** *** 80,95 **** The order of base port and irq line don't matter; you may specify only one, the other will have the value of the compiled-in default. You may also have to install the file-system module `iso9660.o', if you ! didn't compile that into the kernel. (If you use `tcsh' as shell, you ! might consider defining - alias listinstalledmodules 'cat /proc/modules | awk \{print\$1\}' - complete rmmod 'p/1/`listinstalledmodules`/' - alias listcompiledmodules '(cd /usr/src/linux/modules; \ls -o *.o)' - alias insmod 'insmod /usr/src/linux/modules/\!:1 \!:2*' - complete insmod 'p/1/`listcompiledmodules`/' - - which makes typing insmod and rmmod somewhat easier.) Using the driver as part of the kernel -------------------------------------- --- 87,94 ---- The order of base port and irq line don't matter; you may specify only one, the other will have the value of the compiled-in default. You may also have to install the file-system module `iso9660.o', if you ! didn't compile that into the kernel. Using the driver as part of the kernel -------------------------------------- *************** *** 103,108 **** --- 102,117 ---- auto probing is turned off for both settings, thus setting the other value to the compiled-in default. + Note that you can put these parameters also in the lilo configuration file: + + # linux config + image = /vmlinuz + root = /dev/hda1 + label = Linux + append = "cm206=0x340,11" + read-only + + If module parameters and LILO config options don't work ------------------------------------------------------- If autoprobing does not work, you can hard-wire the default values *************** *** 111,117 **** the defines of CM206_IRQ and CM206_BASE. - Mounting the cdrom ------------------ 1) Make sure that there is the right device installed in /dev. --- 120,125 ---- *************** *** 138,152 **** If things don't work -------------------- ! - Try to do a `tail /var/adm/messages' to find out if the driver ! said anything about what is going wrong during the initialization. - Try to do a `dd if=/dev/cm206cd | od -tc | less' to read from the CD. ! - Look in the /proc directory to see if `cm206' shows up under ! one of `interrupts', `ioports', `devices' or `modules' (if ! applicable). DISCLAIMER --- 146,159 ---- If things don't work -------------------- ! - Try to do a `dmesg' to find out if the driver said anything about ! what is going wrong during the initialization. - Try to do a `dd if=/dev/cm206cd | od -tc | less' to read from the CD. ! - Look in the /proc directory to see if `cm206' shows up under one of ! `interrupts', `ioports', `devices' or `modules' (if applicable). DISCLAIMER *************** *** 173,177 **** (c) 1995 David A. van Leeuwen ! The driver is released, like most Linux software, under the conditions ! of the GNU general public license. --- 180,185 ---- (c) 1995 David A. van Leeuwen ! The driver is released under the conditions of the GNU general public ! license, that can be found in the file COPYING in the root of this ! source tree. diff --context --recursive --new-file linux-orig/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile *** linux-orig/drivers/cdrom/Makefile Wed Apr 17 20:18:31 1996 --- linux/drivers/cdrom/Makefile Wed Apr 17 22:40:32 1996 *************** *** 20,25 **** --- 20,35 ---- M_OBJS := MOD_LIST_NAME := CDROM_MODULES + # The following if's should be generalized (ORed) for all drivers that + # use the generic interface of cdrom.c + ifeq ($(CONFIG_CM206),y) + L_OBJS += cdrom.o + else + ifeq ($(CONFIG_CM206),m) + M_OBJS += cdrom.o + endif + endif + ifeq ($(CONFIG_AZTCD),y) L_OBJS += aztcd.o else diff --context --recursive --new-file linux-orig/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c *** linux-orig/drivers/cdrom/cdrom.c Thu Jan 1 01:00:00 1970 --- linux/drivers/cdrom/cdrom.c Wed Apr 17 22:47:51 1996 *************** *** 0 **** --- 1,504 ---- + /* cdrom.c. Common ioctl and open routines for various Linux cdrom drivers. + Copyright (c) 1996 David van Leeuwen. + + The routines in the file should provide an interface between + software accessing cdroms and the various drivers that implement + specific hardware devices. + + */ + + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + + #define VERSION "$Id: cdrom.c,v 0.4 1996/04/17 20:47:50 david Exp $" + + /* Not-exported routines. */ + int cdrom_open(struct inode *ip, struct file *fp); + void cdrom_release(struct inode *ip, struct file *fp); + int cdrom_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg); + int cdrom_media_changed(dev_t dev); + + struct file_operations cdrom_fops = + { + NULL, /* lseek */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir */ + NULL, /* select */ + cdrom_ioctl, /* ioctl */ + NULL, /* mmap */ + cdrom_open, /* open */ + cdrom_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + cdrom_media_changed, /* media_change */ + NULL /* revalidate */ + }; + + static struct cdrom_device_ops *cdromdevs[MAX_BLKDEV] = { + NULL, + }; + + /* This macro makes sure we don't have to check on cdrom_device_ops + * existence in the run-time routines below. Change_capability is a + * hack to have the capability flags defined const, while we can still + * change it here without gcc complaining at every line. + */ + + #define ENSURE(call, bits) if (cdo->call == NULL) *change_capability &= ~(bits) + + /* We don't use $name$ yet, but it could be used for the /proc + * filesystem in the future, or for other purposes. + */ + int register_cdrom(int major, char *name, struct cdrom_device_ops *cdo) + { + int *change_capability = &cdo->capability; /* hack, gcc complains OK */ + + if (major < 0 || major >= MAX_BLKDEV) + return -1; + if (cdo->open_files == NULL || cdo->open == NULL || cdo->release == NULL) + return -2; + ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY); + ENSURE(lock_door, CDC_LOCK); + ENSURE(select_speed, CDC_SELECT_SPEED); + ENSURE(select_disc, CDC_SELECT_DISC); + ENSURE(get_last_session, CDC_MULTI_SESSION); + ENSURE(audio_ioctl, CDC_PLAY_AUDIO); + ENSURE(media_changed, CDC_MEDIA_CHANGED); + cdromdevs[major] = cdo; + cdo->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK; + cdo->mc_flags = 0; + return 0; + } + #undef ENSURE + + int unregister_cdrom(int major, char *name) + { + if (major < 0 || major >= MAX_BLKDEV) + return -1; + if (cdromdevs[major] == NULL) + return -2; + cdromdevs[major] = NULL; + return 0; + } + + /* We need our own cdrom error types! This is a temporary solution. */ + + #define ENOMEDIUM EAGAIN /* no medium in removable device */ + + /* We use the open-option O_NONBLOCK as in indicator of that the + * purpose of opening is only for subsequent ioctl() calls; no device + * integrity checks are performed. + * + * We hope that all cd-player programs will adopt this convention. It + * is in their own advantage: device conotrol becomes a lot easier + * this way. + */ + + int cdrom_open(struct inode *ip, struct file *fp) + { + dev_t dev = ip->i_rdev; + struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)]; + int purpose = !!(fp->f_flags & O_NONBLOCK); + int ret = 0; + + if (cdo == NULL || MINOR(dev) >= cdo->minors) + return -ENODEV; + if (fp->f_mode & SEL_OUT) /* is SEL_OUT by coincidence 0x2? */ + return -EROFS; /* misuse of SEL_OUT? */ + purpose = purpose || !(cdo->options & CDO_USE_FFLAGS); + if (cdo->open_files(dev)) /* already openend? */ + ret = cdo->open(dev, purpose); /* yes: no check */ + else { + if (purpose) /* open for ioctl? */ + ret = cdo->open(dev, purpose); /* yes: no check */ + else { /* no: open for data read */ + if (cdo->drive_status) { + int ds = cdo->drive_status(dev); + if (ds == CDS_TRAY_OPEN) { /* tray open? */ + if (cdo->capability & ~cdo->mask & CDC_CLOSE_TRAY && + cdo->options & CDO_AUTO_CLOSE) { + /* can/may i close it? */ + if (cdo->tray_move(dev, 0)) + return -EIO; + } else + return -ENOMEDIUM; /* can't close: too bad */ + ds = cdo->drive_status(dev); + if (ds == CDS_NO_DISC) + return -ENOMEDIUM; + } + } + if (cdo->disc_status) { + int ds = cdo->disc_status(dev); + if (ds == CDS_NO_DISC) + return -ENOMEDIUM; + if (cdo->options & CDO_CHECK_TYPE && + ds != CDS_DATA_1) + return -ENODATA; + } + /* all is well, we can open the device */ + ret = cdo->open(dev, purpose); /* open for data */ + if (cdo->capability & ~cdo->mask & CDC_LOCK && + cdo->options & CDO_LOCK) + cdo->lock_door(dev, 1); + } + } + return ret; + } + + /* Admittedly, the logic below could be performed in a nicer way. */ + void cdrom_release(struct inode *ip, struct file *fp) + { + dev_t dev = ip->i_rdev; + struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)]; + + if (cdo == NULL || MINOR(dev) >= cdo->minors) + return; + if (cdo->open_files(dev) == 1 && /* last process that closes dev */ + cdo->options & CDO_LOCK && /* we want */ + cdo->capability & ~cdo->mask & CDC_LOCK) /* we can */ + cdo->lock_door(dev, 0); + cdo->release(dev); + if (cdo->open_files(dev) == 0) { /* last process that closes dev */ + sync_dev(dev); + invalidate_buffers(dev); + if (cdo->options & CDO_AUTO_EJECT && + cdo->capability & ~cdo->mask & CDC_OPEN_TRAY) + cdo->tray_move(dev, 1); + } + } + + /* We want to make media_changed accessible to the user through an + * ioctl. The main problem now is that we must double-buffer the + * low-level implementation, to assure that the VFS and the user both + * see a medium change once. + * + * For now, i've implemented only 16 minor devs (half a long), i have to + * think of a better solution... $Queue$ is either 0 or 1. Queue 0 is + * in the lower 16 bits, queue 1 in the higher 16 bits. + */ + + int media_changed(dev_t dev, int queue) + { + unsigned int major = MAJOR(dev); + unsigned int minor = MINOR(dev); + struct cdrom_device_ops *cdo = cdromdevs[major]; + int ret; + unsigned long mask = 1 << (16 * queue + minor); + + queue &= 1; + if (cdo == NULL || minor >= 16) + return -1; + ret = !!(cdo->mc_flags & mask); /* changed since last call? */ + if (cdo->media_changed(dev)) { + cdo->mc_flags |= 0x10001 << minor; /* set bit on both queues */ + ret |= 1; /* and on return value */ + } + cdo->mc_flags &= ~mask; /* clear bit */ + return ret; + } + + int cdrom_media_changed(dev_t dev) + { + struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)]; + if (cdo == NULL || MINOR(dev) >= cdo->minors) + return -ENODEV; + if (cdo->media_changed == NULL) + return -EINVAL; + return media_changed(dev, 0); + } + + /* Requests to the low-level drivers will /always/ be done in the + following format convention: + + CDROM_LBA: all data-related requests. + CDROM_MSF: all audio-related requests. + + However, a low-level implementation is allowed to refuse this + request, and return information in its own favorite format. + + It doesn't make sense /at all/ to ask for a play_audio in LBA + format, or ask for multi-session info in MSF format. However, for + backward compatibility these format requests will be satisfied, but + the requests to the low-level drivers will be sanitized in the more + meaningful format indicated above. + */ + + #undef current /* set in sched.h */ + + void sanitize_format(union cdrom_addr *addr, + u_char * current, u_char requested) + { + if (*current == requested) + return; /* nothing to be done! */ + if (requested == CDROM_LBA) { + addr->lba = (int) addr->msf.frame + + 75 * (addr->msf.second - 2 + 60 * addr->msf.minute); + } else { /* CDROM_MSF */ + int lba = addr->lba; + addr->msf.frame = lba % 75; + lba /= 75; + lba += 2; + addr->msf.second = lba % 60; + addr->msf.minute = lba / 60; + } + *current = requested; + } + + /* All checking and format change makes this code really hard to read! + * So let's make some check and memory move macros. These macros are + * a little inenficient when used both in the same piece of code, as + * verify_area is used twice, but who cares, as ioctl() calls + * shouldn't be in inner loops. + */ + #define GETARG(type, x) { \ + int ret=verify_area(VERIFY_READ, (void *) arg, sizeof x); \ + if (ret) return ret; \ + memcpy_fromfs(&x, (type *) arg, sizeof x); } + #define PUTARG(type, x) { \ + int ret=verify_area(VERIFY_WRITE, (void *) arg, sizeof x); \ + if (ret) return ret; \ + memcpy_tofs((type *) arg, &x, sizeof x); } + + /* Some of the cdrom ioctls are not implemented here, because these + * appear to be either too device-specific, or it is not clear to me + * what use they are. These are (number of drivers that support them + * in parenthesis): CDROMREADMODE1 (2+ide), CDROMREADMODE2 (2+ide), + * CDROMREADAUDIO (2+ide), CDROMREADRAW (2), CDROMREADCOOKED (2), + * CDROMSEEK (2), CDROMPLAYBLK (scsi), CDROMREADALL (1). Read-audio, + * OK (although i guess the record companies aren't too hapy with + * this, most drives therefor refuse to transport audio data). But + * why are there 5 different READs defined? For now, these functions + * are left over to the device-specific ioctl routine, + * cdo->dev_ioctl. Note that as a result of this, no + * memory-verification is performed for these ioctls. + */ + int cdrom_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg) + { + dev_t dev = ip->i_rdev; + struct cdrom_device_ops *cdo = cdromdevs[MAJOR(dev)]; + + if (cdo == NULL || MINOR(dev) >= cdo->minors) + return -ENODEV; + /* the first few commands do not deal with audio capabilities, but + only with routines in cdrom device operations. */ + switch (cmd) { + /* maybe we should order cases after statistics of use? */ + case CDROMMULTISESSION: { + struct cdrom_multisession ms_info; + u_char requested_format; + if (!(cdo->capability & CDC_MULTI_SESSION)) + return -EINVAL; + GETARG(struct cdrom_multisession, ms_info); + requested_format = ms_info.addr_format; + ms_info.addr_format = CDROM_LBA; + cdo->get_last_session(dev, &ms_info); + sanitize_format(&ms_info.addr, &ms_info.addr_format, requested_format); + PUTARG(struct cdrom_multisession, ms_info); + return 0; + } + + case CDROMEJECT: + if (cdo->open_files(dev) == 1 && /* 1: the process that calls EJECT */ + cdo->capability & ~cdo->mask & CDC_OPEN_TRAY) + return cdo->tray_move(dev, 1); + else + return -EINVAL; + + case CDROMCLOSETRAY: + if (cdo->open_files(dev) == 1 && /* 1: the process that calls CLOSE */ + cdo->capability & ~cdo->mask & CDC_CLOSE_TRAY) + return cdo->tray_move(dev, 0); + else + return -EINVAL; + + case CDROMEJECT_SW: + cdo->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); + if (arg) + cdo->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT; + return 0; + + case CDROM_MEDIA_CHANGED: + if (cdo->capability & ~cdo->mask & CDC_MEDIA_CHANGED) + return media_changed(dev, 1); + else + return -EINVAL; + + case CDROM_SET_OPTIONS: + cdo->options |= (int) arg; + return cdo->options; + + case CDROM_CLEAR_OPTIONS: + cdo->options &= ~(int) arg; + return cdo->options; + + case CDROM_SELECT_SPEED: + if (0 <= arg && arg < (int) (cdo->speed + 0.5) && + cdo->capability & ~cdo->mask & CDC_SELECT_SPEED) + return cdo->select_speed(dev, arg); + else + return -EINVAL; + + case CDROM_SELECT_DISC: + if (0 <= arg && arg <= cdo->capacity && + cdo->capability & ~cdo->mask & CDC_SELECT_DISC) + return cdo->select_disc(dev, arg); + else + return -EINVAL; + + /* The following function is implemented, although very few audio + * discs give Universal Product Code information, which should just be + * the Medium Catalog Number on the box. Note, that the way the code + * is written on the CD is /not/ uniform across all discs! + */ + case CDROM_GET_MCN: { + struct cdrom_mcn mcn; + if (!(cdo->capability & CDC_MCN)) + return -EINVAL; + if (!cdo->get_mcn(dev, &mcn)) { + PUTARG(struct cdrom_mcn, mcn); + return 0; + } + return -EINVAL; + } + + case CDROM_DRIVE_STATUS: + if (cdo->drive_status == NULL) + return -EINVAL; + else + return cdo->drive_status(dev); + + case CDROM_DISC_STATUS: + if (cdo->disc_status == NULL) + return -EINVAL; + else + return cdo->disc_status(dev); + + /* The following is not implemented, because there are too many + * different data type. We could support /1/ raw mode, that is large + * enough to hold everything. + */ + + #if 0 + case CDROMREADMODE1: { + struct cdrom_msf msf; + char buf[CD_FRAMESIZE]; + GETARG(struct cdrom_msf, msf); + if (!cdo->read_audio(dev, cmd, &msf, &buf)) { + PUTARG(char *, buf); + return 0; + } + return -EINVAL; + } + #endif + } /* switch */ + + /* Now all the audio-ioctls follow, they are all routed through the + same call audio_ioctl(). */ + + if (cdo->capability & CDC_PLAY_AUDIO) + switch (cmd) { + case CDROMSUBCHNL: { + struct cdrom_subchnl q; + u_char requested, back; + GETARG(struct cdrom_subchnl, q); + requested = q.cdsc_format; + q.cdsc_format = CDROM_MSF; + if (!cdo->audio_ioctl(dev, cmd, &q)) { + back = q.cdsc_format; /* local copy */ + sanitize_format(&q.cdsc_absaddr, &back, requested); + sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); + PUTARG(struct cdrom_subchnl, q); + return 0; + } else + return -EINVAL; + } + case CDROMREADTOCHDR: { + struct cdrom_tochdr header; + GETARG(struct cdrom_tochdr, header); + if (!cdo->audio_ioctl(dev, cmd, &header)) { + PUTARG(struct cdrom_tochdr, header); + return 0; + } else + return -EINVAL; + } + case CDROMREADTOCENTRY: { + struct cdrom_tocentry entry; + u_char requested_format; + GETARG(struct cdrom_tocentry, entry); + requested_format = entry.cdte_format; + entry.cdte_format = CDROM_MSF; /* make interface to low-level uniform */ + if (!(cdo->audio_ioctl(dev, cmd, &entry))) { + sanitize_format(&entry.cdte_addr, &entry.cdte_format, requested_format); + PUTARG(struct cdrom_tocentry, entry); + return 0; + } else + return -EINVAL; + } + case CDROMPLAYMSF: { + struct cdrom_msf msf; + GETARG(struct cdrom_mdf, msf); + return cdo->audio_ioctl(dev, cmd, &msf); + } + case CDROMPLAYTRKIND: { + struct cdrom_ti track_index; + GETARG(struct cdrom_ti, track_index); + return cdo->audio_ioctl(dev, cmd, &track_index); + } + case CDROMVOLCTRL: { + struct cdrom_volctrl volume; + GETARG(struct cdrom_volctl, volume); + return cdo->audio_ioctl(dev, cmd, &volume); + } + case CDROMVOLREAD: { + struct cdrom_volctrl volume; + if (!cdo->audio_ioctl(dev, cmd, &volume)) { + PUTARG(struct cdrom_volctl, volume); + return 0; + } + return -EINVAL; + } + case CDROMSTART: + case CDROMSTOP: + case CDROMPAUSE: + case CDROMRESUME: + return cdo->audio_ioctl(dev, cmd, NULL); + } /* switch */ + if (cdo->dev_ioctl != NULL) /* device specific ioctls? */ + return cdo->dev_ioctl(dev, cmd, arg); + return -EINVAL; + } + + #ifdef MODULE + int init_module(void) + { + printk(KERN_INFO "Module inserted " VERSION "\n"); + return 0; + } + + void cleanup_module(void) + { + printk(KERN_INFO "Module cdrom removed\n"); + } + + #endif + /* + * Local variables: + * tab-width: 4 + * comment-column: 40 + * c-indent-level: 8 + * c-continued-statement-offset: 8 + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux-obj/include -Wall -Wstrict-prototypes -O2 -m486 -c cdrom.c -o cdrom.o" + * End: + */ diff --context --recursive --new-file linux-orig/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c *** linux-orig/drivers/cdrom/cm206.c Wed Apr 17 20:23:25 1996 --- linux/drivers/cdrom/cm206.c Sun Apr 14 22:26:26 1996 *************** *** 1,5 **** /* 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 --- 1,5 ---- /* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card. ! Copyright (c) 1995, 1996 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 *************** *** 54,64 **** 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 --- 54,83 ---- 10 jun 1995: 0.33 Workman still behaves funny, but you should be able to eject and substitute another disc. ! An adaptation of 0.33 is included in linux-1.3.7 by Eberhard Moenkeberg ! 18 jul 1995: 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. + + 18 dec 1995: 0.35 Add some code for error checking... no luck... + + We jump to reach our goal: version 1.0 in the next stable linux kernel. + + 19 mar 1996: 0.95 Different implementation of CDROM_GET_UPC, on + request of Thomas Quinot. + 25 mar 1996: 0.96 Interpretation of opening with O_WRONLY or O_RDWR: + open only for ioctl operation, e.g., for operation of + tray etc. + 4 apr 1996: 0.97 First implementation of layer between VFS and cdrom + driver, a generic interface. Much of the functionality + of cm206_open() and cm206_ioctl() is transferred to a + new file cdrom.c and its header ucdrom.h. + + Upgrade to Linux kernel 1.3.78. + + 11 apr 1996 0.98 Upgrade to Linux kernel 1.3.85 + More uniformization stuff. * * Parts of the code are based upon lmscd.c written by Kai Petzke, * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin *************** *** 73,85 **** * 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" #include --- 92,104 ---- * 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 "$Id: cm206.c,v 0.99 1996/04/14 20:26:26 david Exp $" #include *************** *** 94,103 **** --- 113,130 ---- #include #include + #include + #include #define MAJOR_NR CM206_CDROM_MAJOR #include + + #undef DEBUG + #define STATISTICS /* record times and frequencies of events */ + #undef AUTO_PROBE_MODULE + #define USE_INSW + #include /* This variable defines whether or not to probe for adapter base port *************** *** 109,119 **** 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 */ --- 136,141 ---- *************** *** 121,133 **** #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 --- 143,158 ---- #define UART_TIMEOUT (5*HZ/100) #define DSB_TIMEOUT (7*HZ) /* time for the slowest command to finish */ + #define LINUX_BLOCK_SIZE 512 /* WHERE is this defined? */ #define RAW_SECTOR_SIZE 2352 /* ok, is also defined in cdrom.h */ #define ISO_SECTOR_SIZE 2048 + #define BLOCKS_ISO (ISO_SECTOR_SIZE/LINUX_BLOCK_SIZE) /* 4 */ + #define CD_SYNC_HEAD 16 /* CD_SYNC + CD_HEAD */ #ifdef STATISTICS /* keep track of errors in counters */ ! #define stats(i) { ++cd->stats[st_ ## i]; \ ! cd->last_stat[st_ ## i] = cd->stat_counter++; \ ! } #else #define stats(i) (void) 0 #endif *************** *** 141,147 **** typedef unsigned char uch; /* 8-bits */ typedef unsigned short ush; /* 16-bits */ ! struct toc_struct{ uch track, fsm[3], q0; }; --- 166,172 ---- typedef unsigned char uch; /* 8-bits */ typedef unsigned short ush; /* 16-bits */ ! struct toc_struct{ /* private copy of Table of Contents */ uch track, fsm[3], q0; }; *************** *** 174,179 **** --- 199,205 ---- 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 */ + uch media_changed; /* record if media changed */ }; #define DISC_STATUS cd->disc_status[0] *************** *** 182,188 **** #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. */ --- 208,214 ---- #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; /* the main memory structure */ /* First, we define some polling functions. These are actually only being used in the initialization. */ *************** *** 221,282 **** as there seems so reason for this to happen. */ ! static void cm206_interrupt(int sig, void *dev_id, 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 appropriate */ ! } ! /* 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); } --- 247,310 ---- as there seems so reason for this to happen. */ ! static void cm206_interrupt(int sig, void *dev_id, 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 */ ! if (cd->intr_ls & ls_attention) stats(attention); ! /* 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); } *************** *** 446,451 **** --- 474,493 ---- return 0; } + #ifdef USE_INSW + #define transport_data insw + #else + /* this routine implements insw(,,). There was a time i had the + impression that there would be any difference in error-behaviour. */ + void transport_data(int port, ush * dest, int count) + { + int i; + ush * d; + for (i=0, d=dest; ibackground) { *************** *** 462,468 **** 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); --- 504,511 ---- stop_read(); return -3; } ! transport_data(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); *************** *** 514,525 **** --- 557,574 ---- } } + /* This command clears the dsb_possible_media_change flag, so we must + * retain it. + */ 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]; + cd->media_changed |= + !!(cd->dsb & (dsb_possible_media_change | + dsb_drive_not_ready | dsb_tray_not_closed)); } void get_disc_status(void) *************** *** 529,583 **** } } ! 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; } --- 578,607 ---- } } ! /* The new open. The real opeining strategy is defined in cdrom.c. */ ! ! static int cm206_open(dev_t dev, int purpose) { ! if (!cd->openfiles) { /* reset only first time */ cd->background=0; reset_cm260(); cd->adapter_last = -1; /* invalidate adapter memory */ cd->sector_last = -1; } ++cd->openfiles; MOD_INC_USE_COUNT; stats(open); return 0; } ! static void cm206_release(dev_t dev) { if (cd->openfiles==1) { if (cd->background) { cd->background=0; stop_read(); } cd->sector_last = -1; /* Make our internal buffer invalid */ ! FIRST_TRACK = 0; /* No valid disc status */ } --cd->openfiles; MOD_DEC_USE_COUNT; } *************** *** 587,593 **** 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 */ --- 611,617 ---- void empty_buffer(int sectors) { while (sectors>=0) { ! transport_data(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 */ *************** *** 598,604 **** 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) --- 622,628 ---- cd->sector_last=cd->adapter_first; /* update the buffer sector */ } ! /* try_adapter. This function determines if the requested sector is is in adapter memory, or will appear there soon. Returns 0 upon success */ int try_adapter(int sector) *************** *** 648,665 **** } 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; --- 672,689 ---- } error=0; for (i=0; inr_sectors; i++) { ! cd_sec_no = (CURRENT->sector+i)/BLOCKS_ISO; /* 4 times 512 bytes */ ! quarter = (CURRENT->sector+i) % BLOCKS_ISO; ! dest = CURRENT->buffer + i*LINUX_BLOCK_SIZE; /* is already in buffer memory? */ if (cd->sector_first <= cd_sec_no && cd_sec_no < cd->sector_last) { ! source = ((uch *) cd->sector) + 16 + quarter*LINUX_BLOCK_SIZE ! + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE; ! memcpy(dest, source, LINUX_BLOCK_SIZE); } else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) { ! source = ((uch *) cd->sector)+16+quarter*LINUX_BLOCK_SIZE; ! memcpy(dest, source, LINUX_BLOCK_SIZE); } else { error=1; *************** *** 669,695 **** } } - 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 --- 693,698 ---- *************** *** 753,763 **** 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; --- 756,761 ---- *************** *** 782,787 **** --- 780,786 ---- if (!cd->toc[track].track) get_toc_lba(track); } + /* return 0 upon success */ int read_toc_header(struct cdrom_tochdr * hp) { if (!FIRST_TRACK) get_disc_status(); *************** *** 792,800 **** 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) --- 791,799 ---- 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 0; } ! return -1; } void play_from_to_msf(struct cdrom_msf* msfp) *************** *** 859,978 **** 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); --- 858,911 ---- else if (PAUSED) qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED; else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; ! return 0; } + void invalidate_toc(void) + { + memset(cd->toc, 0, sizeof(cd->toc)); + memset(cd->disc_status, 0, sizeof(cd->disc_status)); + } + + /* cdrom.c guarantees that cdte_format == CDROM_MSF */ void get_toc_entry(struct cdrom_tocentry * ep) { uch track = normalize_track(ep->cdte_track); update_toc_entry(track); ! 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]; ep->cdte_adr = cd->toc[track].q0 & 0xf; ep->cdte_ctrl = cd->toc[track].q0 >> 4; ep->cdte_datamode=0; } ! /* Audio ioctl. Ioctl commands connected to audio are in such an ! * idiosyncratic i/o format, that we leave these untouched. Return 0 ! * upon success. Memory checking has been done by cdrom_ioctl(), the ! * calling function, as well as LBA/MSF sanatization. ! */ ! int cm206_audio_ioctl(dev_t dev, unsigned int cmd, void * arg) { switch (cmd) { ! case CDROMREADTOCHDR: ! return read_toc_header((struct cdrom_tochdr *) arg); ! case CDROMREADTOCENTRY: ! get_toc_entry((struct cdrom_tocentry *) arg); return 0; ! case CDROMPLAYMSF: ! play_from_to_msf((struct cdrom_msf *) arg); return 0; ! case CDROMPLAYTRKIND: /* admittedly, not particulary beautiful */ ! play_from_to_track(((struct cdrom_ti *)arg)->cdti_trk0, ! ((struct cdrom_ti *)arg)->cdti_trk1); return 0; case CDROMSTOP: PAUSED=0; if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1); ! else return 0; case CDROMPAUSE: + get_drive_status(); if (cd->dsb & dsb_play_in_progress) { type_0_command(c_stop, 1); type_1_command(c_audio_status, 5, cd->audio_status); *************** *** 983,1044 **** 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 --- 916,1096 ---- if (PAUSED) play_from_to_track(0,0); PAUSED=0; return 0; case CDROMSTART: case CDROMVOLCTRL: return 0; ! case CDROMSUBCHNL: ! return get_current_q((struct cdrom_subchnl *)arg); ! default: ! return -EINVAL; ! } ! } ! ! /* Ioctl. These ioctls are specific to the cm206 driver. I have made ! some driver statistics accessible through ioctl calls. ! */ ! ! static int cm206_ioctl(dev_t dev, 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 default: debug(("Unknown ioctl call 0x%x\n", cmd)); return -EINVAL; } } ! int cm206_media_changed(dev_t dev) ! { ! if (cd != NULL) { ! int r; ! get_drive_status(); /* ensure cd->media_changed OK */ ! r = cd->media_changed; ! cd->media_changed = 0; /* clear bit */ ! return r; ! } ! else return -EIO; ! } ! ! /* The new generic cdrom support. Routines should be consice, most of ! the logic should be in cdrom.c */ ! ! /* returns number of times device is in use */ ! int cm206_open_files(dev_t dev) ! { ! if (cd) return cd->openfiles; ! return -1; ! } ! ! /* controls tray movement */ ! int cm206_tray_move(dev_t dev, int position) ! { ! if (position) { /* 1: eject */ ! type_0_command(c_open_tray,1); ! invalidate_toc(); ! } ! else type_0_command(c_close_tray, 1); /* 0: close */ ! return 0; ! } ! ! /* gives current state of the drive */ ! int cm206_drive_status(dev_t dev) ! { ! get_drive_status(); ! if (cd->dsb & dsb_tray_not_closed) return CDS_TRAY_OPEN; ! if (!(cd->dsb & dsb_disc_present)) return CDS_NO_DISC; ! if (cd->dsb & dsb_drive_not_ready) return CDS_DRIVE_NOT_READY; ! return CDS_DISC_OK; ! } ! ! /* gives current state of disc in drive */ ! int cm206_disc_status(dev_t dev) ! { ! uch xa; ! get_drive_status(); ! if ((cd->dsb & dsb_not_useful) | !(cd->dsb & dsb_disc_present)) ! return CDS_NO_DISC; ! get_disc_status(); ! if (DISC_STATUS & cds_all_audio) return CDS_AUDIO; ! xa = DISC_STATUS >> 4; ! switch (xa) { ! case 0: return CDS_DATA_1; /* can we detect CDS_DATA_2? */ ! case 1: return CDS_XA_2_1; /* untested */ ! case 2: return CDS_XA_2_2; ! } ! return 0; ! } ! ! /* locks or unlocks door lock==1: lock; return 0 upon success */ ! int cm206_lock_door(dev_t dev, int lock) ! { ! uch command = (lock) ? c_lock_tray : c_unlock_tray; ! type_0_command(command, 1); /* wait and get dsb */ ! /* the logic calculates the success, 0 means successful */ ! return lock ^ ((cd->dsb & dsb_tray_locked) != 0); ! } ! ! /* Although a session start should be in LBA format, we return it in ! MSF format because it is slightly easier, and the new generic ioctl ! will take care of the necessary conversion. */ ! int cm206_get_last_session(dev_t dev, struct cdrom_multisession * mssp) ! { ! if (!FIRST_TRACK) get_disc_status(); ! if (mssp != NULL) { ! if (DISC_STATUS & cds_multi_session) { /* multi-session */ ! 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->addr_format = CDROM_MSF; ! mssp->xa_flag = 1; ! } else { ! mssp->xa_flag = 0; ! } ! return 1; ! } ! return 0; ! } ! ! int cm206_get_upc(dev_t dev, struct cdrom_mcn * mcn) ! { ! uch upc[10]; ! char * ret = mcn->medium_catalog_number; ! int i; ! ! if (type_1_command(c_read_upc, 10, upc)) return -EIO; ! for (i=0; i<13; i++) { ! int w=i/2+1, r=i%2; ! if (r) ret[i] = 0x30 | (upc[w] & 0x0f); ! else ret[i] = 0x30 | ((upc[w] >> 4) & 0x0f); ! } ! ret[13] = '\0'; ! return 0; ! } ! ! int cm206_reset(dev_t dev) ! { ! 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; ! invalidate_toc(); ! return 0; ! } ! ! static struct cdrom_device_ops cm206_dops = { ! cm206_open, /* open */ ! cm206_release, /* release */ ! cm206_open_files, /* number of open_files */ ! cm206_drive_status, /* drive status */ ! cm206_disc_status, /* disc status */ ! cm206_media_changed, /* media changed */ ! cm206_tray_move, /* tray move */ ! cm206_lock_door, /* lock door */ ! NULL, /* select speed */ ! NULL, /* select disc */ ! cm206_get_last_session, /* get last session */ ! cm206_get_upc, /* get universal product code */ ! cm206_reset, /* hard reset */ ! cm206_audio_ioctl, /* audio ioctl */ ! cm206_ioctl, /* device-specific ioctl */ ! CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_MULTI_SESSION | ! CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO, /* capability */ ! 0, /* mask flags */ ! 2.0, /* maximum speed */ ! 1, /* number of minor devices */ ! 1, /* number of discs */ ! 0, /* options, ignored */ ! 0 /* mc_flags, ignored */ }; /* This routine gets called during init if thing go wrong, can be used *************** *** 1047,1054 **** { switch (level) { case 4: if (unregister_blkdev(MAJOR_NR, "cm206")) { ! printk("Can't unregister cm206\n"); return; } case 3: --- 1099,1110 ---- { switch (level) { case 4: + if (unregister_cdrom(MAJOR_NR, "cm206")) { + printk("Can't unregister cdrom cm206\n"); + return; + } if (unregister_blkdev(MAJOR_NR, "cm206")) { ! printk("Can't unregister major cm206\n"); return; } case 3: *************** *** 1074,1098 **** 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; --- 1130,1145 ---- int probe_base_port(int base) { int b=0x300, e=0x370; /* this is the range of start addresses */ ! volatile int fool, i; if (base) b=e=base; for (base=b; base<=e; base += 0x10) { if (check_region(base, 0x10)) continue; ! for (i=0; i<3; i++) ! fool = inw(base+2); /* empty possibly uart_receive_buffer */ if((inw(base+6) & 0xffef) != 0x0001 || /* line_status */ (inw(base) & 0xad00) != 0) /* data status */ continue; return(base); } return 0; *************** *** 1106,1112 **** 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 */ --- 1153,1159 ---- sti(); irqs = probe_irq_on(); reset_cm260(); /* causes interrupt */ ! udelay(100); /* 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 */ *************** *** 1119,1125 **** uch e=0; long int size=sizeof(struct cm206_struct); ! printk(KERN_INFO "cm206: v" VERSION); cm206_base = probe_base_port(auto_probe ? 0 : cm206_base); if (!cm206_base) { printk(" can't find adapter!\n"); --- 1166,1172 ---- uch e=0; long int size=sizeof(struct cm206_struct); ! printk(KERN_INFO VERSION); cm206_base = probe_base_port(auto_probe ? 0 : cm206_base); if (!cm206_base) { printk(" can't find adapter!\n"); *************** *** 1162,1172 **** 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? */ init_bh(CM206_BH, cm206_bh); --- 1209,1224 ---- return -EIO; } printk(".\n"); ! if (register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 0) { printk("Cannot register for major %d!\n", MAJOR_NR); cleanup(3); return -EIO; } + if (register_cdrom(MAJOR_NR, "cm206", &cm206_dops) != 0) { + printk("Cannot register for cdrom %d!\n", MAJOR_NR); + cleanup(3); + return -EIO; + } blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = 16; /* reads ahead what? */ init_bh(CM206_BH, cm206_bh); *************** *** 1235,1237 **** --- 1287,1294 ---- } } #endif /* MODULE */ + /* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/include/linux -Wall -Wstrict-prototypes -O2 -m486 -c cm206.c -o cm206.o" + * End: + */ diff --context --recursive --new-file linux-orig/include/linux/cm206.h linux/include/linux/cm206.h *** linux-orig/include/linux/cm206.h Wed Apr 17 20:23:56 1996 --- linux/include/linux/cm206.h Sun Apr 14 14:38:57 1996 *************** *** 2,7 **** --- 2,10 ---- Copyright (c) 1995 David van Leeuwen */ + #ifndef LINUX_CM206_H + #define LINUX_CM206_H + /* First, the cm260 stuff */ /* The ports and irq used. Although CM206_BASE and CM206_IRQ are defined below, the values are not used unless autoprobing is turned off and *************** *** 48,58 **** #define dc_mask_transmit_ready 0x100 #define dc_flag_enable 0x80 #define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \ dc_mask_transmit_ready) /* now some constants related to the cm206 */ ! /* another drive status byte, echoed by the cm206 on most commands */ #define dsb_error_condition 0x1 #define dsb_play_in_progress 0x4 --- 51,62 ---- #define dc_mask_transmit_ready 0x100 #define dc_flag_enable 0x80 + /* Define the default data control register flags here */ #define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \ dc_mask_transmit_ready) /* now some constants related to the cm206 */ ! /* another drive status byte, echoed by the cm206 on most commmands */ #define dsb_error_condition 0x1 #define dsb_play_in_progress 0x4 *************** *** 126,128 **** --- 130,175 ---- #define CM206CTL_GET_STAT 0x2000 #define CM206CTL_GET_LAST_STAT 0x2001 + + /* for kernel 1.2.n */ + #if !defined(CDROM_GET_UPC) + #define CDROM_GET_UPC 0x5311 + #define CDROMRESET 0x5312 + #endif + + #ifdef STATISTICS + + /* This is an ugly way to guarantee that the names of the statistics + * are the same in the code and in the diagnostics program. */ + + #ifdef __KERNEL__ + #define x(a) st_ ## a + #define y enum + #else + #define x(a) #a + #define y char * stats_name[] = + #endif + + y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error), + x(crc_error), x(sync_error), x(lost_intr), x(echo), + x(write_timeout), x(receive_timeout), x(read_timeout), + x(dsb_timeout), x(stop_0xff), x(back_read_timeout), + x(sector_transferred), x(read_restarted), x(read_background), + x(bh), x(open), x(ioctl_multisession), x(attention) + #ifdef __KERNEL__ + , x(last_entry) + #endif + }; + + #ifdef __KERNEL__ + #define NR_STATS st_last_entry + #else + #define NR_STATS (sizeof(stats_name)/sizeof(char*)) + #endif + + #undef y + #undef x + + #endif STATISTICS + + #endif LINUX_CM206_H diff --context --recursive --new-file linux-orig/include/linux/stats206.h linux/include/linux/stats206.h *** linux-orig/include/linux/stats206.h Sat Jul 1 18:06:00 1995 --- linux/include/linux/stats206.h Thu Jan 1 01:00:00 1970 *************** *** 1,34 **** - /* stats206.h. Define constants to gather statistics on the cm206 behavior. - Copyright (c) 1995 David van Leeuwen. - - This is published under the Gnu Public Licence, read the header in - the file cm206.c. - */ - - /* This is an ugly way to guarantee that the names of the statistics - * are the same in the code and in the diagnostics program. */ - - #ifdef __KERNEL__ - #define x(a) st_ ## a - #define y enum - #else - #define x(a) #a - #define y char * stats_name[] = - #endif - - y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error), - x(crc_error), x(sync_error), x(lost_intr), x(echo), - x(write_timeout), x(receive_timeout), x(read_timeout), - x(dsb_timeout), x(stop_0xff), x(back_read_timeout), - x(sector_transferred), x(read_restarted), x(read_background), - x(bh), x(open), x(ioctl_multisession) - #ifdef __KERNEL__ - , x(last_entry) - #endif - }; - - #ifdef __KERNEL__ - #define NR_STATS st_last_entry - #else - #define NR_STATS sizeof(stats_name)/sizeof(char*) - #endif --- 0 ---- diff --context --recursive --new-file linux-orig/include/linux/ucdrom.h linux/include/linux/ucdrom.h *** linux-orig/include/linux/ucdrom.h Thu Jan 1 01:00:00 1970 --- linux/include/linux/ucdrom.h Wed Apr 17 22:21:40 1996 *************** *** 0 **** --- 1,96 ---- + /* ucdrom.h. Uniform cdrom data structures for cdrom.c. -*- c -*- + Copyright (c) 1996 David van Leeuwen. + */ + + #ifndef LINUX_UCDROM_H + #define LINUX_UCDROM_H + + struct cdrom_device_ops { + /* routines */ + int (*open) (dev_t, int); + void (*release) (dev_t); + int (*open_files) (dev_t); /* number of open files */ + int (*drive_status) (dev_t); + int (*disc_status) (dev_t); + int (*media_changed) (dev_t); + int (*tray_move) (dev_t, int); + int (*lock_door) (dev_t, int); + int (*select_speed) (dev_t, int); + int (*select_disc) (dev_t, int); + int (*get_last_session) (dev_t, struct cdrom_multisession *); + int (*get_mcn) (dev_t, struct cdrom_mcn *); + int (*reset) (dev_t dev); /* hard reset device */ + int (*audio_ioctl) (dev_t, unsigned int, void *); /* play stuff */ + int (*dev_ioctl) (dev_t, unsigned int, unsigned long); /* dev-specific */ + /* specifications */ + const int capability; /* capability flags */ + int mask; /* mask of capability: disables them */ + const float speed; /* maximum speed for reading data + 1 means audio-speed (150 kB/s) */ + const int minors; /* number of minor devs supported */ + const int capacity; /* number of discs in jukebox */ + /* device-related storage */ + int options; /* options flags */ + long mc_flags; /* media change buffer flags (2*16) */ + }; + + /* capability flags */ + #define CDC_CLOSE_TRAY 0x1 /* caddy systems /can't/ close */ + #define CDC_OPEN_TRAY 0x2 /* but /can/ eject. */ + #define CDC_LOCK 0x4 /* disable manual eject */ + #define CDC_SELECT_SPEED 0x8 /* programmable speed */ + #define CDC_SELECT_DISC 0x10 /* select disc from juke-box */ + #define CDC_MULTI_SESSION 0x20 /* read sessions>1 */ + #define CDC_MCN 0x40 /* Medium Catalog Number */ + #define CDC_MEDIA_CHANGED 0x80 /* media changed */ + #define CDC_PLAY_AUDIO 0x100 /* audio functions */ + + /* drive status possibilities */ + #define CDS_NO_INFO 0 /* if not implemented */ + #define CDS_NO_DISC 1 + #define CDS_TRAY_OPEN 2 + #define CDS_DRIVE_NOT_READY 3 + #define CDS_DISC_OK 4 + + /* disc status possibilities, other than CDS_NO_DISC */ + #define CDS_AUDIO 100 + #define CDS_DATA_1 101 + #define CDS_DATA_2 102 + #define CDS_XA_2_1 103 + #define CDS_XA_2_2 104 + + /* User-configurable behavior options */ + #define CDO_AUTO_CLOSE 0x1 /* close tray on first open() */ + #define CDO_AUTO_EJECT 0x2 /* open tray on last release() */ + #define CDO_USE_FFLAGS 0x4 /* use O_NONBLOCK information on open */ + #define CDO_LOCK 0x8 /* lock tray on open files */ + #define CDO_CHECK_TYPE 0x10 /* check type on open for data */ + + /* Some more ioctls to control these options */ + #define CDROM_SET_OPTIONS 0x5320 + #define CDROM_CLEAR_OPTIONS 0x5321 + #define CDROM_SELECT_SPEED 0x5322 /* head-speed */ + #define CDROM_SELECT_DISC 0x5323 /* for juke-boxes */ + #define CDROM_MEDIA_CHANGED 0x5325 + #define CDROM_DRIVE_STATUS 0x5326 /* tray position, etc. */ + #define CDROM_DISC_STATUS 0x5327 /* disc type etc. */ + + /* Rename and old ioctl */ + #define CDROM_GET_MCN CDROM_GET_UPC /* medium catalog number */ + + /* the general file operations structure: */ + extern struct file_operations cdrom_fops; + + extern int register_cdrom(int major, char *name, + struct cdrom_device_ops *cdo); + extern int unregister_cdrom(int major, char *name); + + #endif /* LINUX_UCDROM_H */ + /* + * Local variables: + * tab-width: 4 + * comment-column: 40 + * c-indent-level: 8 + * c-continued-statement-offset: 8 + * End: + */