%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%									%
%	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{Character Device Drivers}

{\bf [Write appropriate blurb here]}

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

Besides functions defined by the {\tt file\_operations} structure,
there is at least one other function that you will have to write, the
{\tt foo\_init()} function.  You will have to change {\tt
chr\_dev\_init()} in chr\_drv/mem.c to call your {\tt foo\_init()}
function.  {\tt foo\_init()} will take one argument, {\tt long
mem\_start}, which will be the address of the current end of allocated
memory.  If your driver needs to allocate more than 4K of contiguous
space at runtime, here is the place.  Simply save {\tt mem\_start} in
an appropriate variable, add however much space you need to {\tt
mem\_start}, and return the new value.  Your driver will now have
exclusive access to the memory between the old and new values of {\tt
mem\_start}.

{\tt foo\_init()} should first call {\tt register\_chrdev()} to
register itself and avoid device number contention.  {\tt
register\_chrdev()} takes three arguments:
\begin{dispitems}
\item[{\tt int major}] This is the major number which the driver
wishes to allocate.
\item[{\tt char *name}] This is the symbolic name of the driver.  It
is currently not used for anything, but this may change in the future.
\item[{\tt struct file\_operations *f\_ops}] This is the address of
your {\tt file\_operations} structure defined in
Section~\ref{file-operations}
\item[{\bf Returns:}] 0 if no other character device has registered
with the same major number.\\
non-0 if the call fails, presumably because another character device
has already allocated that major number.
\end{dispitems}

Generally, the {\tt foo\_init()} routine will then attempt to detect
the hardware that it is supposed to be driving.  It should make sure
that all necessary data structures are filled out for all present
hardware, and have some way of ensuring that non-present hardware does
not get accessed.  {\bf [detail different ways of doing this.]}

\subsection{Interrupts vs. Polling}\label{int-poll-char}

In a polling driver, the {\tt foo\_read()} and {\tt foo\_write()}
functions are pretty easy to write.  Here is an example of {\tt
foo\_read()}:
\begin{screen}\begin{verbatim}
static int foo_write(struct inode * inode, struct file * file,
                     char * buf, int count)
{
    unsigned int minor = MINOR(inode->i_rdev);
    char ret;

    while (count > 0) {
        ret = foo_write_byte(minor);
	if (ret < 0) {
            foo_handle_error(WRITE, ret, minor);
            continue;
        }
        buf++ = ret; count--
    }
    return count;
}
\end{verbatim}\end{screen}
{\tt foo\_write\_byte()} and {\tt foo\_handle\_error()} are either
functions defined elsewhere in foo.c or pseudocode.  {\tt WRITE} would
be a constant or {\tt \#define}.

It should be clear from this example how to code the {\tt foo\_read()}
function as well.

Interrupt-driven drivers are a little more difficult.  Here is an
example of a {\tt foo\_write()} that is interrupt-driven:
\begin{screen}\begin{verbatim}
static int foo_write(struct inode * inode, struct file * file,
                     char * buf, int count)
{
    unsigned int minor = MINOR(inode->i_rdev);
    unsigned long copy_size;
    unsigned long total_bytes_written = 0;
    unsigned long bytes_written;
    struct foo_struct *foo = &foo_table[minor];

    do {
        copy_size = (count <= FOO_BUFFER_SIZE ? count : FOO_BUFFER_SIZE);
        memcpy_fromfs(foo->foo_buffer, buf, copy_size);

        while (copy_size) {
            /* initiate interrupts */

            if (some_error_has_occured)  {
                /* handle error condition */
            }

            current->timeout = jiffies + FOO_INTERRUPT_TIMEOUT;
                /* set timeout in case an interrupt has been missed */
            interruptible_sleep_on(&foo->foo_wait_queue);
            bytes_written = foo->bytes_xfered;
            foo->bytes_written = 0;
            if (current->signal & ~current->blocked) {
                if (total_bytes_written + bytes_written)
                    return total_bytes_written + bytes_written;
                else
                    return -EINTR; /* nothing was written, system
                                      call was interrupted, try again */
            }
        }

        total_bytes_written += bytes_written;
        buf += bytes_written;
        count -= bytes_written;

    } while (count > 0);

    return total_bytes_written;
}

static void foo_interrupt(int irq)
{
    struct foo_struct *foo = &foo_table[foo_irq[irq]];

    /* Here, do whatever actions ought to be taken on an interrupt.
       Look at a flag in foo_table to know whether you ought to be
       reading or writing. */

    /* Increment foo->bytes_xfered by however many characters were
       read or written */

    if (buffer too full/empty)
        wake_up_interruptible(&foo->foo_wait_queue);
}


\end{verbatim}\end{screen}

Again, a {\tt foo\_read()} function is written analagously.  {\tt
foo\_table[]} is an array of structures, each of which has several
members, some of which are {\tt foo\_wait\_queue} and {\tt
bytes\_xfered}, which can be used for both reading and writing.  {\tt
foo\_irq[]} is an array of 16 integers, and is used for looking up
which entry in {\tt foo\_table[]} is associated with the {\tt irq}
generated and reported to the {\tt foo\_interrupt()} function.

To tell the interrupt-handling code to call {\tt foo\_interrupt()},
you need to use either {\tt request\_irq()} or {\tt irqaction()}.
This is either done when {\tt foo\_open()} is called, or if you want
to keep things simple, when {\tt foo\_init()} is called.
{\tt request\_irq()} is the simpler of the two, and works rather like
an old-style signal handler.  It takes two arguments: the first is the
number of the {\tt irq} you are requesting, and the second is a
pointer to your interrupt handler, which must take an integer argument
(the irq that was generated) and have a return type of {\tt void}.
{\tt request\_irq()} returns {\tt -EINVAL} if {\tt irq} $>$ 15 or if
the pointer to the interrupt handler is {\tt NULL}, {\tt -EBUSY} if
that interrupt has already been taken, or 0 on success.

{\tt irqaction()} works rather like the user-level {\tt sigaction()},
and in fact reuses the {\tt sigaction} structure.  The {\tt
sa\_restorer()} field of the sigaction structure is not used, but
everything else is the same.  See the entry for {\tt irqaction()} in
Section~\ref{sec-dev-funcs}, {\bf Supporting Functions}, for further
information about {\tt irqaction()}.

\subsection{TTY drivers}

{\bf [The reasons that this section has not been written are that I
don't know enough about TTY stuff yet and that right now, Linux is
pretty dependant on the fact that only major number 4 holds any TTY
devices.  All these major numbers are pretty much taken up already.
Somebody needs to do something about this, but until then, there's not
much to be done.]}

\subsection{Example Drivers}

In this section, we will examine some real \linux\ device drivers that
are actually a part of the kernel.  This is a rather long section, but
I think a worthwhile one.  {\bf [Or will be rather long, when I
re-write it to be actually useful\dots]}