\documentclass{article}
\def\version{$Id: cdrom-standard.tex,v 1.9 1997/12/28 15:42:49 david Exp $}
\newcommand{\newsection}[1]{\newpage\section{#1}}
\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 cd-rom}}
\def\UCD{{\sc Uniform cd-rom Driver}}
\def\cdromc{{\tt {cdrom.c}}}
\def\cdromh{{\tt {cdrom.h}}}
\def\fo{\sl} % foreign words
\def\ie{{\fo i.e.}}
\def\eg{{\fo e.g.}}
\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@ElseWare.cistron.nl}
\\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}}
\\{\footnotesize updated by Jens Axboe {\tt(axboe@image.dk)}}}
\date{12 March 1999}
\maketitle
\newsection{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 hardware devices available for the many platforms
that \linux\ now supports (\ie, i386-PCs, Sparc Suns, etc.)
\item
The open design of the operating system, such that anybody can write a
driver for \linux.
\item
There is plenty of source code around as examples of how to write a driver.
\end{itemize}
The openness of \linux, and the many different types of available
hardware has allowed \linux\ to support many different hardware devices.
Unfortunately, the very openness that has allowed \linux\ to support
all these different devices has also allowed the behavior of each
device driver to differ significantly from one device to another.
This divergence of behavior has been very significant for \cdrom\
devices; the way a particular drive reacts to a `standard' $ioctl()$
call varies greatly from one device driver to another. To avoid making
their drivers totally inconsistent, the writers of \linux\ \cdrom\
drivers generally created new device drivers by understanding, copying,
and then changing an existing one. Unfortunately, this practice did not
maintain uniform behavior across all the \linux\ \cdrom\ drivers.
This document describes an effort to establish Uniform behavior across
all the different \cdrom\ device drivers for \linux. This document also
defines the various $ioctl$s, and how the low-level \cdrom\ device
drivers should implement them. Currently (as of the \linux\ 2.1.$x$
development kernels) several low-level \cdrom\ device drivers, including
both IDE/ATAPI and SCSI, now use this Uniform interface.
When the \cdrom\ was developed, the interface between the \cdrom\ drive
and the computer was not specified in the standards. As a result, many
different \cdrom\ interfaces were developed. Some of them had their
own proprietary design (Sony, Mitsumi, Panasonic, Philips), other
manufacturers adopted an existing electrical interface and changed
the functionality (CreativeLabs/SoundBlaster, Teac, Funai) or simply
adapted their drives to one or more of the already existing electrical
interfaces (Aztech, Sanyo, Funai, Vertos, Longshine, Optics Storage and
most of the `NoName' manufacturers). In cases where a new drive really
brought its own interface or used its own command set and flow control
scheme, either a separate driver had to be written, or an existing
driver had to be enhanced. History has delivered us \cdrom\ support for
many of these different interfaces. Nowadays, almost all new \cdrom\
drives are either IDE/ATAPI or SCSI, and it is very unlikely that any
manufacturer will create a new interface. Even finding drives for the
old proprietary interfaces is getting difficult.
When (in the 1.3.70's) I looked at the existing software interface,
which was expressed through \cdromh, it appeared to be a rather wild
set of commands and data formats.\footnote{I cannot recollect what
kernel version I looked at, then, presumably 1.2.13 and 1.3.34---the
latest kernel that I was indirectly involved in.} It seemed that many
features of the software interface had been added to accommodate the
capabilities of a particular drive, in an {\fo ad hoc\/} manner. More
importantly, it appeared that the behavior of the `standard' commands
was different for most of the different drivers: \eg, some drivers
close the tray if an $open()$ call occurs when the tray is open, while
others do 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 their drivers'
behavior was usually different.
I decided to start a discussion on how to make all the \linux\ \cdrom\
drivers behave more uniformly. I began by contacting the developers of
the many \cdrom\ drivers found in the \linux\ kernel. Their reactions
encouraged me to write the \UCD\ which this document is intended to
describe. The implementation of the \UCD\ is in the file \cdromc. This
driver is intended to be an additional software layer that sits on top
of the low-level device drivers for each \cdrom\ drive. By adding this
additional layer, it is possible to have all the different \cdrom\
devices behave {\em exactly\/} the same (insofar as the underlying
hardware will allow).
The goal of the \UCD\ is {\em not\/} to alienate driver developers who
have not yet taken steps to support this effort. The goal of \UCD\ is
simply to give people writing application programs for \cdrom\ drives
{\em one\/} \linux\ \cdrom\ interface with consistent behavior for all
\cdrom\ devices. In addition, this also provides a consistent interface
between the low-level device driver code and the \linux\ kernel. Care
is taken that 100\,\% compatibility exists with the data structures and
programmer's interface defined in \cdromh. This guide was written to
help \cdrom\ driver developers adapt their code to use the \UCD\ code
defined in \cdromc.
Personally, I think that the most important hardware interfaces are
the IDE/ATAPI drives and, of course, the SCSI drives, but as prices
of hardware drop continuously, it is also likely that people may have
more than one \cdrom\ drive, possibly of mixed types. It is important
that these drives behave in the same way. 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 obsolete and IDE/ATAPI drives became the
standard. At the time of the last update to this document (November
1997) it is becoming difficult to even {\em find} anything less than a
16 speed \cdrom\ drive, and 24 speed drives are common.
\newsection{Standardizing through another software level}
\label{cdrom.c}
At the time this document was conceived, all drivers directly
implemented the \cdrom\ $ioctl()$ calls through their own routines. This
led to the danger of different drivers forgetting to do important things
like checking that the user was giving the driver valid data. More
importantly, this led to the divergence of behavior, which has already
been discussed.
For this reason, the \UCD\ was created to enforce consistent \cdrom\
drive behavior, and to provide a common set of services to the various
low-level \cdrom\ device drivers. The \UCD\ now provides another
software-level, that separates the $ioctl()$ and $open()$ implementation
from the actual hardware implementation. Note that this effort has
made few changes which will affect a user's application programs. The
greatest change involved moving the contents of the various low-level
\cdrom\ drivers' header files to the kernel's cdrom directory. This was
done to help ensure that the user is only presented with only one cdrom
interface, the interface defined in \cdromh.
\cdrom\ drives are specific enough (\ie, different from other
block-devices such as floppy or hard disc drives), to define a set
of common {\em \cdrom\ device operations}, $<cdrom-device>_dops$.
These operations are different from the classical block-device file
operations, $<block-device>_fops$.
The routines for the \UCD\ interface level are implemented in the file
\cdromc. In this file, the \UCD\ interfaces with the kernel as a block
device 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, since this file is the
place where the behavior of all \cdrom-devices is defined and
standardized. The actual interface to the various types of \cdrom\
hardware is still performed by various low-level \cdrom-device
drivers. These routines simply implement certain {\em capabilities\/}
that are common to all \cdrom\ (and really, all removable-media
devices).
Registration of a low-level \cdrom\ device driver is now done through
the general routines in \cdromc, not through the Virtual File System
(VFS) any more. The interface implemented in \cdromc\ is carried out
through two general structures that contain information about the
capabilities of the driver, and the specific drives on which the
driver operates. The structures are:
\begin{description}
\item[$cdrom_device_ops$]
This structure contains information about the low-level driver for a
\cdrom\ device. This structure is conceptually connected to the major
number of the device (although some drivers may have different
major numbers, as is the case for the IDE driver).
\item[$cdrom_device_info$]
This structure contains information about a particular \cdrom\ drive,
such as its device name, speed, etc. This structure is conceptually
connected to the minor number of the device.
\end{description}
Registering a particular \cdrom\ drive with the \UCD\ is done by the
low-level device driver though a call to:
$$register_cdrom(struct\ cdrom_device_info * <device>_info)
$$
The device information structure, $<device>_info$, contains all the
information needed for the kernel to interface with the low-level
\cdrom\ device driver. One of the most important entries in this
structure is a pointer to the $cdrom_device_ops$ structure of the
low-level driver.
The device operations structure, $cdrom_device_ops$, contains a list
of pointers to the functions which are implemented in the low-level
device driver. When \cdromc\ accesses a \cdrom\ device, it does it
through the functions in this structure. It is impossible to know all
the capabilities of future \cdrom\ drives, so it is expected that this
list may need to be expanded from time to time as new technologies are
developed. For example, CD-R and CD-R/W drives are beginning to become
popular, and support will soon need to be added for them. For now, the
current $struct$ is:
$$
\halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}&
$/*$ \rm# $*/$\hfil\cr
struct& cdrom_device_ops\ \{ \hidewidth\cr
&int& (* open)(struct\ cdrom_device_info *, int)\cr
&void& (* release)(struct\ cdrom_device_info *);\cr
&int& (* drive_status)(struct\ cdrom_device_info *, int);\cr
&int& (* media_changed)(struct\ cdrom_device_info *, int);\cr
&int& (* tray_move)(struct\ cdrom_device_info *, int);\cr
&int& (* lock_door)(struct\ cdrom_device_info *, int);\cr
&int& (* select_speed)(struct\ cdrom_device_info *, int);\cr
&int& (* select_disc)(struct\ cdrom_device_info *, int);\cr
&int& (* get_last_session) (struct\ cdrom_device_info *,
struct\ cdrom_multisession *{});\cr
&int& (* get_mcn)(struct\ cdrom_device_info *, struct\ cdrom_mcn *{});\cr
&int& (* reset)(struct\ cdrom_device_info *);\cr
&int& (* audio_ioctl)(struct\ cdrom_device_info *, unsigned\ int,
void *{});\cr
&int& (* dev_ioctl)(struct\ cdrom_device_info *, unsigned\ int,
unsigned\ long);\cr
\noalign{\medskip}
&const\ int& capability;& capability flags \cr
&int& n_minors;& number of active minor devices \cr
\};\cr
}
$$
When a low-level device driver implements one of these capabilities,
it should add a function pointer to this $struct$. When a particular
function is not implemented, however, this $struct$ should contain a
NULL instead. The $capability$ flags specify the capabilities of the
\cdrom\ hardware and/or low-level \cdrom\ driver when a \cdrom\ drive
is registered with the \UCD. The value $n_minors$ should be a positive
value indicating the number of minor devices that are supported by
the low-level device driver, normally~1. Although these two variables
are `informative' rather than `operational,' they are included in
$cdrom_device_ops$ because they describe the capability of the {\em
driver\/} rather than the {\em drive}. Nomenclature has always been
difficult in computer programming.
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$ is used. For most
drivers, the main parameter is the $struct$ $cdrom_device_info$, from
which the major and minor number can be extracted. (Most low-level
\cdrom\ drivers don't even look at the major and minor number though,
since many of them only support one device.) This will be available
through $dev$ in $cdrom_device_info$ described below.
The drive-specific, minor-like information that is registered with
\cdromc, currently contains the following fields:
$$
\halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}&
$/*$ \rm# $*/$\hfil\cr
struct& cdrom_device_info\ \{ \hidewidth\cr
& struct\ cdrom_device_ops *& ops;& device operations for this major\cr
& struct\ cdrom_device_info *& next;& next device_info for this major\cr
& void *& handle;& driver-dependent data\cr
\noalign{\medskip}
& kdev_t& dev;& device number (incorporates minor)\cr
& int& mask;& mask of capability: disables them \cr
& int& speed;& maximum speed for reading data \cr
& int& capacity;& number of discs in a jukebox \cr
\noalign{\medskip}
&int& options : 30;& options flags \cr
&unsigned& mc_flags : 2;& media-change buffer flags \cr
& int& use_count;& number of times device is opened\cr
& char& name[20];& name of the device type\cr
\}\cr
}$$
Using this $struct$, a linked list of the registered minor devices is
built, using the $next$ field. The device number, the device operations
struct and specifications of properties of the drive are stored in this
structure.
The $mask$ flags can be used to mask out some of the capabilities listed
in $ops\to capability$, if a specific drive doesn't support a feature
of the driver. The value $speed$ specifies the maximum head-rate of the
drive, measured in units of normal audio speed (176\,kB/sec raw data or
150\,kB/sec file system data). The value $n_discs$ should reflect the
number of discs the drive can hold simultaneously, if it is designed
as a juke-box, or otherwise~1. The parameters are declared $const$
because they describe properties of the drive, which don't change after
registration.
A few registers contain variables local to the \cdrom\ drive. 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 users' 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. Other
data that is specific to a minor drive, can be accessed through $handle$,
which can point to a data structure specific to the low-level driver.
The fields $use_count$, $next$, $options$ and $mc_flags$ need not be
initialized.
The intermediate software layer that \cdromc\ forms will perform some
additional bookkeeping. The use count of the device (the number of
processes that have the device opened) is registered in $use_count$. 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. Two functions {\em must\/} be implemented, namely
$open()$ and $release()$. 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{$Int\ open(struct\ cdrom_device_info * cdi, int\ purpose)$}
$Open()$ should try to open the device for a specific $purpose$, which
can be either:
\begin{itemize}
\item[0] Open for reading data, as done by {\tt {mount()}} (2), or the
user commands {\tt {dd}} or {\tt {cat}}.
\item[1] Open for $ioctl$ commands, as done by audio-CD playing
programs.
\end{itemize}
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, such as spinning
up the disc, etc. % and device-use count
\subsection{$Void\ release(struct\ cdrom_device_info * cdi)$}
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()$.
This is the only function returning type $void$.
\subsection{$Int\ drive_status(struct\ cdrom_device_info * cdi, int\ slot_nr)$}
\label{drive status}
The function $drive_status$, if implemented, should provide
information on the status of the drive (not the status of the disc,
which may or may not be in the drive). If the drive is not a changer,
$slot_nr$ should be ignored. In \cdromh\ 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{$Int\ media_changed(struct\ cdrom_device_info * cdi, int\ disc_nr)$}
This function is very similar to the original function in $struct\
file_operations$. It returns 1 if the medium of the device $cdi\to
dev$ has changed since the last call, and 0 otherwise. The parameter
$disc_nr$ identifies a specific slot in a juke-box, it should be
ignored for single-disc drives. 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 (\eg, an auto-mounting daemon).
\subsection{$Int\ tray_move(struct\ cdrom_device_info * cdi, 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{$Int\ lock_door(struct\ cdrom_device_info * cdi, 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}
This function returns 0 upon success, and a non-zero value upon
error. Note that if the door is already in the requested state, no
action need be taken, and the return value should be 0.
\subsection{$Int\ select_speed(struct\ cdrom_device_info * cdi, int\ speed)$}
Some \cdrom\ drives are capable of changing their head-speed. There
are several reasons for changing the speed of a \cdrom\ drive. Badly
pressed \cdrom s may benefit from less-than-maximum head rate. Modern
\cdrom\ drives can obtain very high head rates (up to $24\times$ is
common). It has been reported that these drives can make reading
errors at these high speeds, reducing the speed can prevent data loss
in these circumstances. Finally, some of these drives can
make an annoyingly loud noise, which a lower speed may reduce. %Finally,
%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.
This function specifies the speed at which data is read or audio is
played back. The value of $speed$ specifies the head-speed of the
drive, measured in units of standard cdrom speed (176\,kB/sec raw data
or 150\,kB/sec file system data). So to request that a \cdrom\ drive
operate at 300\,kB/sec you would call the CDROM_SELECT_SPEED $ioctl$
with $speed=2$. The special value `0' means `auto-selection', \ie,
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.
\subsection{$Int\ select_disc(struct\ cdrom_device_info * cdi, int\ number)$}
If the drive can store multiple discs (a juke-box) this function
will perform disc selection. It should return the number of the
selected disc on success, a negative value on error. Currently, only
the ide-cd driver supports this functionality.
\subsection{$Int\ get_last_session(struct\ cdrom_device_info * cdi, struct\
cdrom_multisession * ms_info)$}
This function should implement the old corresponding $ioctl()$. For
device $cdi\to 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 transformation if
necessary. The return value is 0 upon success.
\subsection{$Int\ get_mcn(struct\ cdrom_device_info * cdi, 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{$Int\ reset(struct\ cdrom_device_info * cdi)$}
This call should perform a hard-reset on 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. If the drive is no
longer listening, it may be wise for the underlying low-level cdrom
driver to time out.
\subsection{$Int\ audio_ioctl(struct\ cdrom_device_info * cdi, unsigned\
int\ cmd, void * arg)$}
Some of the \cdrom-$ioctl$s defined in \cdromh\ 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 to be accessed through a
single function, repeating the arguments $cmd$ and $arg$. Note that
the latter is of type $void*{}$, rather than $unsigned\ long\