There are two main classes of device which the kernel needs to be able to access: those with a simple character based interface, like keyboards and printers, and those with a data block based interface like hard disks, floppy disks and CD-ROMs. As the names imply, character based devices send and receive data in single character chunks, whereas block based devices, in order to improve speed and efficiency, do their data transfers a whole data buffer full at a time.
From this, it should come as no surprise that there are also basically two kinds of device driver called character device drivers and block device drivers. The major difference between the two is that system calls involving character devices are almost directly associated with functions inside the device driver, whereas reads and writes to block devices interact primarily with the buffer cache mechanism, only calling block device driver routines when it is necessary to perform physical input and output. Figure 1 shows how these pieces fit together.
Generally, a different device driver is required for each type of device that needs to be controlled. So, for example, there is a device driver to control floppy disks and it knows how to interface with all the standard floppy disk types and sizes. Indeed, if you had a system with more than one floppy disk drive installed then the same device driver would be used for them all.
The main functions of a Linux device driver are:
In order to access a device you need to be able to treat it like a file. This means that there must be a file name for all devices in the directory hierarchy somewhere, so that you can use the open(), close(), etc., system calls on them.
These files are called character device special files and block device special files and, by convention, they are stored in the directory /dev. Some of the files in that directory might be:
brw-rw---- 1 root floppy 2, 0 Jul 18 1994 fd0 brw-rw---- 1 root floppy 2, 1 Jul 18 1994 fdl brw-rw---- 1 root disk 3, 0 Jul 18 1994 hda brw-rw---- 1 root disk 3, 1 Jul 18 1994 hdal brw-rw---- 1 root disk 3, 2 Jul 18 1994 hda2 brw-rw---- 1 root disk 3, 3 Jul 18 1994 hda3 brw-rw---- 1 root disk 3, 4 Jul 18 1994 hda4 crw-rw---- 1 root daemon 6, 0 Jul 18 1994 ip0 crw-rw-rw- 1 root sys 1, 3 Jul 18 1994 null crw-rw-rw- 1 root tty 5, 0 Jul 18 1994 tty crw--w--w- 1 pc book 4, 0 Jul 18 1994 tty0 crw--w--w- 1 pc book 4, 1 Aug 30 15:16 ttyl
Notice that these files have a type, given by the first character on each line, of either c or b, meaning character or block special file respectively. Also notice that the special files have a pair of comma-separated numbers just before the date, where ordinary files would display their file size. The first of these two numbers is called the major device number of the device and the second number is called the minor device number of the device.
The major device numbers for block and character devices are used to identify the particular device driver whose internal functions will be called to deal with I/O requests for this device. The minor device number will be made available to the device driver routines and will be used by them to identify to which particular device the current I/O request relates. For example, the previous listing shows both floppy disk 0 and floppy disk 1 (fd0 and fdl) to have block major device number 2. This identifies the device driver to call when I/O requests are taken for /dev/fd0 and /dev/fdl. Inside the device driver the minor device numbers (0 for fd0 and 1 for fdl) will be used to tell the two drives apart.
The device special files are created with the mknod command (or the mknod() system call). Only root can create these files. The general form of the mknod command is:
mknod filename type major minor
Where filename is the path to the special file to be created, type is either c or b for character or block files respectively, and major and minor are the major and minor device numbers to associate with the file. For example:
# mknod /dev/myttyl c 4 1
will create the character special file Idevimyttyl with major device number four and minor device number zero. As this major/minor combination already exists (/dev/ttyl) all that this command has done is to create an alternative special file which may be used to access the same device.