%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%									%
%	Copyright (C) 1992, 1993 Michael K. Johnson,			%
%	johnsonm@SunSITE.unc.edu					%
%									%
%	This file is freely copyable, but you must preserve this	%
%	copyright notice on all copies, it must only be distributed	%
%	as part of the Linux Kernel Hackers' Guide, and its use is	%
%	is subject to the conditions expressed in the copyright for	%
%	the whole guide, in the file prelim/copyright.tex		%
%									%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Block Device Drivers}

To mount a filesystem on a device, it must be a block device.  This
means that the device must be a random access device, not a stream
device.  In other words, you must be able to seek to any location on
the physical device at any time.

You do not provide {\tt read()} and {\tt write()} routines for a block
device.  Instead, you use {\tt block\_read()} and {\tt block\_write()},
which are generic functions which will call a {\bf strategy} routine
which you provide.  This strategy routine is also called by the {\bf
buffer cache}(See section ??), which is called by the VFS routines
(See chapter ??) which is how normal files on normal filesystems are
read and written.

These requests are given to a routine called {\tt ll\_rw\_block()},
which constructs lists of requests ordered by an {\bf elevator
algorithm,} which sorts the lists to make accesses faster and more
efficient.

Note that although SCSI disks and CDROMs are considered block devices,
they are handled specially.  Refer to section~\ref{sec-scsi}, Writing a SCSI
Driver, for details.\footnote{Although SCSI didsks and CDROMs are
block devices, SCSI tapes, like other tapes, are generally used as
character devices.  However, they are in kernel/blk\_drv/scsi like all
the other SCSI devices for the sake of convenience.}

\subsection{Initialization}\label{block-initialization}

Initialization of block devices is a bit more complex than
initialization of character devices, especially as some
``initialization'' has to be done at compile time.  There is also a
{\tt register\_blkdev()} call that corresponds to the character device
{\tt register\_chrdev()} call, which the driver must call to say that
it is present, working, and active.

\subsubsection{The file blk.h}

At the top of your driver code, after all other included header files,
you need to write two lines of code:
\begin{tscreen}
\#define MAJOR\_NR \cparam{n}\\
\#include "blk.h"
\end{tscreen}
where \cparam{n} is the major number of your device.
kernel/blk\_drv/blk.h requires the useof the MAJOR\_NR define to set
up many other defines and macros for your driver.

Now you need to edit blk.h.  Under {\tt \#ifdef MAJOR\_NR}, there is a
section of defines that are conditionally included for certain major
numbers, protected by {\tt \#elif (MAJOR\_NR == \cparam{n})}.  At the
end of this list, you will add another section for your driver.  In
that section, the following lines are required:
\begin{tscreen}
\#define DEVICE\_NAME "{\sl device}"\\
\#define DEVICE\_REQUEST do\_{\sl dev}\_request\\
\#define DEVICE\_ON(device) /* {\sl usually blank, see below} */\\
\#define DEVICE\_OFF(device) /* {\sl usually blank, see below} */\\
\#define DEVICE\_NR(device) (MINOR(device))
\end{tscreen}

{\tt DEVICE\_NAME} is simply the device name.  See the other entries
in blk.h for examples.

{\tt DEVICE\_REQUEST} is your strategy routine, which will do all the
I/O on the device.  See section~\ref{block-strategy-routine} for more
details on the strategy routine.

{\tt DEVICE\_ON} and {\tt DEVICE\_OFF} are for devices that need to be
turned on and off, like foppies.  In fact, the floppy driver is
currently the only device driver which uses these defines.

{\tt DEVICE\_NR(device)} is used to determine the number of the
physical device from the minor device number.  For instance, in the
{\tt hd} driver, since the second hard drive starts at minor 64, {\tt
DEVICE\_NR(device)} is defined to be {\tt (MINOR(device)>>6)} instead.

If your driver is interrupt-driven, you will also set
\begin{tscreen}
\#define DEVICE\_INTR do\_{\sl dev}
\end{tscreen}
which will become a variable automatically defined and used by the
remainder of blk.h, specifically by the {\tt SET\_INTR()} and {\tt
CLEAR\_INTR} macros.

You might also consider setting these defines:
\begin{tscreen}
\#define DEVICE\_TIMEOUT {\sl DEV}\_TIMER\\
\#define TIMEOUT\_VALUE {\sl n}
\end{tscreen}
where {\sl n} is the number of jiffies (clock ticks; hundredths of a
second on \linux/386) to time out after if no interrupt is received.
These are used if your device can become ``stuck'': a condition where
the driver waits indefinitely for an interrupt that will never arrive.
If you define these, they will automatically be used in {\tt
SET\_INTR} to make your driver time out.  Of course, your driver will
have to be able to handle the possibility of being timed out by a
timer.  See section ?? for an explanation of how to do this.

\subsubsection{Recognizing PC standard partitions}

{\bf [Inspect the routines in genhd.c and include detailed, correct
instructions on how to use them to allow your device to use the
standard dos partitioning scheme.]}

\subsection{The Buffer Cache}\label{block-buffer-cache}

{\bf [Here, it should be explained briefly how {\tt ll\_rw\_block()}
is called, about getblk() and bread() and breada() and bwrite(), etc.
A real explanation of the buffer cache is reserved for the VFS
reference section, where something on the complexity order of Bach's
treatment of the buffer cache should exist.

For now, we assume that the reader understands the concepts behind the
buffer cache.  If you are a reader and don't, please email me and I'll
help you, which will also help me put my thoughts together for that
section.]}

\subsection{The Strategy Routine}\label{block-strategy-routine}

All reading and writing of blocks is done through the {\bf strategy
routine}.  This routine takes no arguments and returns nothing, but it
knows where to find a list of requests for I/O ({\tt CURRENT}, defined
by default as {\tt blk\_dev[MAJOR\_NR].current\_request}), and knows
how to get data from the device into the blocks.  It is called with
interrupts {\bf disabled} so as to avoid race conditions, and is
responsible for turning on interrupts with a call to {\tt sti()}
before returning.

The strategy routine first calls the {\tt INIT\_REQUEST} macro, which
makes sure that requests are really on the request list and does some
other sanity checking.  {\tt add\_request()} will have already sorted
the requests in the proper order according to the elevator algorithm
(using an insertion sort, as it is called once for every request), so
the strategy routine ``merely'' has to satisfy the request, call {\tt
end\_request(1)}, which will take the request off the list, and then
if there is still another request on the list, satisfy it and call
{\tt end\_request(1)}, until there are no more requests on the list,
at which time it returns.

If the driver is interrupt-driven, the strategy routine need only
schedule the first request to occur, and have the interrupt-handler
call {\tt end\_request(1)} and the call the strategy routine again, in
order to schedule the next request.  If the driver is not
interrupt-driven, the strategy routine may not return until all I/O is
complete.

If for some reason I/O fails permanently on the current request, {\tt
end\_request(0)} must be called to destroy the request.

A request may be for a read or write.  The driver determines whether a
request is for a read or write by examining {\tt CURRENT->cmd}.  If
{\tt CURRENT->cmd == READ}, the request is for a read, and if
{\tt CURRENT->cmd == WRITE}, the request is for a write.  If the
device has seperate interrupt routines for handling reads and writes,
{\tt SET\_INTR({\sl n})} must be called to assure that the proper
interrupt routine will be called.

{\bf [Here I need to include samples of both a polled strategy routine
and an interrupt-driven one.  The interrupt-driven one should provide
seperate read and write interrupt routines to show the use of {\tt
SET\_INTR}.]}

\subsection{Example Drivers}

{\bf [I'm not sure this belongs here~--- we'll see.  I'll leave the
stub here for now.]}
