Making hardware work is tedious. To write to a hard disk, for example, requires that you write magic numbers in magic places, wait for the hard drive to say that it is ready to receive data, and then feed it the data it wants, very carefully. To write to a floppy disk is even harder, and requires that the program supervise the floppy disk drive almost constantly while it is running.
Instead of putting code in each application you write to control each device, you share the code between applications. To make sure that that code is not compromised, you protect it from users and normal programs that use it. If you do it right, you will be able to add and remove devices from your system without changing your applications at all. Furthermore, you need to be able to load your program into memory and run it, which the operating system also does. So an operating system is essentially a priviledged, general, sharable library of low-level hardware and memory and process control functions and routines.
All versions of Unix have an abstract way of reading and writing devices. By making the devices act as much as possible like regular files, the same calls (read(), write(), etc.) can be used for devices and files. Within the kernel, there are a set of functions, registered with the filesystem, which are called to handle requests to do I/O on ``device special files,'' which are those which represent devices. (See mknod(1,2) for an explanation of how to make these files.)
All devices controlled by the same device driver are given the same major number, and of those with the same major number, different devices are distinguished by different minor numbers. (This is not strictly true, but it is close enough. If you understand where it is not true, you don't need to read this section, and if you don't but want to learn, read the code for the tty devices, which uses up 2 major numbers, and may use a third and possibly fourth by the time you read this. Also, the ``misc'' major device supports many minor devices that only need a few minor numbers; we'll get to that later.)
This chapter explains how to write any type of Linux device driver that you might need to, including character, block, SCSI, and network drivers. It explains what functions you need to write, how to initialize your drivers and obtain memory for them efficiently, and what function are built in to Linux to make your job easier.
Creating device drivers for Linux is easier than you might think. It merely involves writing a few functions and registering them with the Virtual Filesystem Switch (VFS), so that when the proper device special files are accessed, the VFS can call your functions.
However, a word of warning is due here: Writing a device driver is writing a part of the Linux kernel. This means that your driver runs with kernel permissions, and can do anything it wants to: write to any memory, reformat your hard drive, damage your monitor or video card, or even break your dishes, if your dishwasher is controlled by your computer. Be careful.
Also, your driver will run in kernel mode, and the Linux kernel, like most Unix kernels, is non-pre-emptible. This means that if you driver takes a long time to work without giving other programs a chance to work, your computer will appear to ``freeze'' when your driver is running. Normal user-mode pre-emptive scheduling does not apply to your driver.
Copyright (C) 1992, 1993, 1994, 1996 Michael K. Johnson,