Device Drivers

Contents

Devices
Device independence
Uniform naming
Device independant software
Device controller
Interrupt handlers
Block devices
Character devices
Other
Device typing
Buffering
Canonical mode

Devices

The O/S at the lowest level must control access to the various hardware devices. These include

Goals

Device independence

The majority of processes should not care what type of device I/O is done from. For example, the ``tr'' program will translate characters whether they come from a file on floppy or hard disk, from a keyboard, from a pipeline, or from a network device.

Uniform naming

The naming conventions for devices should not be dependent on the type of device. In Unix, for example, all devices are accessed by ordinary path names within the file system. There may be conventions for device naming within such a system. For example, in Unix
/dev/tty*       terminals
/dev/fd*        floppy fisk
/dev/mt*        magnetic tape
/dev/st*        streaming tape
/dev/mouse      mouse

Abstraction layers

To handle the variety of devices in an independent manner requires several layers. These are
Device abstraction

Device independant software

Function calls such as ``printf'', ``getchar'' etc are clearly part of the I/O system. However, most of what these function calls do is at a software level, divorced from the actual devices themselves. For example, the call
printf("the value of %s is %d",
                str, n)
goes through these steps: The last step is the only one that requires knowledge of the device, and this is passed down to the next layer.

Frequently this layer insulates itself from the device by using buffers in kernel space. The layer reads and writes to these buffers, and the device driver layer is responsible for filling or emptying these.
Device buffers

Device controller

The device itself will come with some controller card. This typically gives a set of registers or memory and allows a set of operations to be performed on it.

This set of operations depends on the device and cannot be generalised.

Interrupt handlers

When the device does something, it will often take quite some time over it (depending on device and operation). It will signal the end of this by generating an interrupt.

The process that is using the device can do one of two things: wait until the device completes (block) or continue asynchronously (non-blocking).

In either case, something must happen when the interrupt occurs. If the process has blocked, then it can be woken up. If the process continued then a signal should be sent.
Sleeping on a device


Waking on a device
Note that a context switch may have occurred before the interrupt occurs. This means that the device driver cannot leave any information on the user stack or in user space because that may not be accessible. This makes writing device drivers a bit tricky.

Device driver

The responsibility of this layer is to convert device independent calls into device dependent operations for the device controller. Example device independent calls are Devices are often categorised further:

Block devices

A block device is one that stores information in fixed size blocks. Common block sizes are 128 bytes to 1k bytes. Each block may be read independently of the others, so the device allows random access to each block.

Typical block devices are disks.

Additional operations on block devices will include

Character devices

Character devices deliver or accept a stream of characters (bytes) without regard to any other structure.

Such devices include terminals, mice, etc.

Other

The above classification is not rigid. There may be devices that do not fit into either. For example, in Unix System V there are streams that are devices that implement queue-ing devices.

Device typing

If a device is represented by a file in the file system, then there must be some way of knowing that in fact it is not an ordinary file.

This information is maintained (in Unix) by information in the inode. A field labels the device type. See this by ``ls -l'' on files in /dev

crw-rw-rw- root 22,   0 fb
brw-rw-rw- root 16,   2 fd0
``fb'' (frame buffer) is a character device, ``fd0'' is a block device. The numbers ``22, 0'' are the major and minor device numbers respectively. These identify which device driver it actually is.

Terminal drivers

Terminal drivers typically maintain two buffers in kernel space, one for input and one for output. These are maintained as queues so that the correct order is maintained.

When the device interrupts to say that data is ready to read, the data is placed on the incoming queue.

Buffering

The purpose of the input and output buffers is firstly to allow for different rates of process and physical I/O. Nevertheless, if they get too out of step data may be lost. For example, data coming in when the input buffer is full may be lost.

If buffering was not used, then the reading process would have to be ready to process it immediately and finish processing it before the next character arrived. Typically, the input device would be much slower than the process. What should the process do?

If it is blocked, then on each new arrival, it would have to be the active process. This would involve a context switch on every character read. If it is ``busy waiting'', then time would be wasted.

On a multi-tasking system either way wastes time. So the norm on multi-tasking systems is to buffer character I/O. MSDOS would not need to buffer though.

Canonical mode

when the backspace character is pressed, what should happen? The expectation is that it should erase the currently showing character (if on a screen) and move the cursor back.

Who should do this? If the process should do it, then it would have to be aware of each character as it was typed. This would require unbuffered input.

The device driver could do it, because it needs to handle characters as they arrive anyway. In this case the terminal driver does input mode processing.

If the device driver does this processing, then the process ultimately reading from the device would not want to see the original data but the processed data.

The terminal drivers would maintain two input queues: the one containing the raw data and the one containing the canonical data. There are three modes that the driver can be in:

The output side of this is that in cooked and rare modes the processing is done, but in raw mode no output processing is done.

Unix Terminal Driver API

Contents

General control
Special properties

General control

Unix uses the function ``ioctl'' to change terminal driver behaviour. For example Because the set of operations is open-ended and poorly typed, this is being replaced with a set of individual functions.

Here is a program that uses the ioctl interface to the terminal driver to control echoing of characters.

#include <stdio.h>
#include <unistd.h>
#include <stropts.h>
#include <termio.h>   

const int SIZE = 128;

int main(void)
{
    struct termio tty_params;
    short flags;
    char buff[SIZE];

    ioctl(0, TCGETA, &tty_params);
    /* save echo and other state */
    flags = tty_params.c_lflag;

    /* turn off echoing */
    tty_params.c_lflag &= ~ECHO;
    ioctl(0, TCSETA, &tty_params);

    printf("Enter password\n");
    fgets(buff, SIZE, stdin);
    printf("Secret password was %s\n", 
           buff);

    /* restore previous state */
    tty_params.c_lflag = flags;
    ioctl(0, TCSETA, &tty_params);

    exit(0);
}    


Termios

There are so many different devices with so many different control features that ioctl has ended up as a mess. The parameters can be lots of different types, and even the number of arguments can change.

For terminals, a new interface based on the termios structure has been devised. Functions using this are tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed, tcgetp- grp, tcsetpgrp

These basically are used in the same way: tcgetattr() instead of ioctl(..., TCGETA, ...), tcsetattr() instead of ioctl(..., TCSETA, ...)

Special properties

Physical terminals differ in their characteristics and what you can do with them. Some terminals allow special sequences to be sent to them to control their behaviour, such as clearing text to the end of line, moving around the screen, etc.

MSDOS often uses special device drivers such as ANSI.SYS. Unix removes this from the device driver and places it in the device independent section. Unix maintains a database of terminal properties in the termcap and terminfo databases. If a process wishes to use a special sequence, it uses device independent functions to look up this database and prepare the appropriate sequence. This is then sent to the normal device driver. The functions tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs are used to access the terminfo database.

Systems Software Home
Jan Newmarch (http://jan.newmarch.name) <jan@newmarch.name>

Copyright © Jan Newmarch
Last modified: Wed May 14 22:01:58 EST 2003