Writing a Linux Driver

The main goal of this article is to learn what a driver is, how to implement a driver for Linux and how to integrate it into the operating system—an article for the experienced C programmer.
Low-Level Programming

Access to the hardware interface (the card) is provided through low-memory addressing. The I/O registers of the card, where we can read/write information, are physically connected to memory addresses of the PC (i.e., ports). For instance, the motor/sonar card of the MRV-4 mobile robot is associated with the address 0x1b0. Sixteen registers are used in this card, so the port map includes addresses from 0x1b0 to 0x1be. A typical list of port addressing is shown in Table 1.:

Table 1. Typical Port Addresses

A free address region must be found to allocate the ports for the new card. In Table 1, addresses from 1b0 to 1be were free. The source code example, foo.c, is available on the SSC FTP site (see end of article) and includes a call to a system function that allows us to see the previous table of addresses. Finally, access to the ports is granted via the functions inb_p and outb_p.

Interrupts are the other main topic when talking about low-level programming and hardware control. Interrupt handling versus polling has the main advantage that hardware is usually slow. We cannot stop all processes in a computer until a printer finishes a job. Instead, we can continue with normal work until the printer finishes, then send an interrupt signal that is handled by a specific function.

Continuing with our example, we need three handlers, one for each of the hardware interrupts that the card can generate: sonars handler (at irq 0x0a), home handler (at irq 0x0b) and bumper handler (at irq 0x0c). As example of what the source code must do, we show the structure of the sonar_irq_hdlr function. Each time an echo from a sonar is received, it must:

  1. Disable hardware interrupts.

  2. Read sonar value from its port and store it in a driver internal variable.

  3. Enable interrupts again.

If a user program wants to read the incoming data from the sonars, it must perform a mrv4_read operation, which returns the data stored in the internal variables of the driver.

Implementation of Driver Functions

Although we will explain the guidelines to implement each of the driver functions, when programming your own driver it is a good idea to use the driver most similar to yours as an example. In our case, the models for mrv4.c and mrv4.h are lp.c and lp.h, respectively.

The file mrv4.c includes the initialisation and I/O functions. The initialisation function mrv4_init must follow these steps (see guidelines in file foo.c):

  1. Check in the device.

  2. Get a free region for port addressing.

  3. Test if hardware is present.

  4. Test if irq numbers are free.

  5. Initialise driver internal variables.

  6. Return an OK status.

If an error is detected in any of these steps, it must undo all previous operations and return an error status. To implement the I/O functions, the following structure (or similar) must be defined and initialized in mrv4.c:

static struct   file_operations mrv4_fops = {
NULL,   /* mrv4_lseek   */
mrv4_read,      /* mrv4_read    */
mrv4_write,     /* mrv4_write   */
NULL,   /* mrv4_readdir */
NULL,   /* mrv4_select  */
mrv4_ioctl,     /* mrv4_ioctl   */
NULL,   /* mrv4_mmap    */
mrv4_open,      /* mrv4_open    */
mrv4_release    /* mrv4_release */

Pointers to all existent I/O functions must be set in this structure. Then, the I/O function code can be implemented, following the guidelines shown in the sidebar.

The available commands are defined in the file mrv4.h (see guidelines in file foo.h also available on the FTP site):

#define MRV4_MAGIC, 0x07
#define MRV4_RESET      _IO(MRV4_MAGIC, 0x01
#define MRV4_GOTOHOME   _IO(MRV4_MAGIC, 0x02
#define MRV4_RESETHOME  _IO(MRV4_MAGIC, 0x03
#define MRV4_JOYSTICK   _IOW(MRV4_MAGIC, 0x04,
        unsigned int
#define MRV4_PREPMOVE   _IOW(MRV4_MAGIC, 0x05,
        unsigned int
#define MRV4_INITODOM   _IO(MRV4_MAGIC, 0x06
#define MRV4_SONTOFIRE  _IOW(MRV4_MAGIC, 0x07,
unsigned int

The _IO macro is used for commands without arguments. The _IOW is used for commands with input arguments. In this case, the macro needs the argument type, for example a pointer might be of type unsigned int. The magic number must be chosen by the programmer. Try to select one not reserved by the system (see other header files at /usr/include/linux). Constants are defined in the file /usr/include/linux/mrv4.h, which must be included by both the driver (mrv4.c) and the user programs. In general, the mrv4.h file can include:

  • Constants and macros definitions

  • ioctl commands

  • Port names

  • Type definitions

  • Data structures to be exchanged between the driver and the user

  • mrv4_init() function prototype



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

I like this one

Anonymous's picture

Good introduction, I think, for those who is not familiar with Linux drivers at all.
Thanks a lot to the author. I enjoyed the article.

This article twice uses the

Anonymous's picture

This article twice uses the term "protected mode" where it should be using the term "supervisor mode".

Protected mode is a mode of the Intel x86 processor which provides various protection features, such as memory protection and the ability to disable privileged instructions. Under Linux, all software runs in protected mode, but user applications run at a different privilege level to the kernel.

Of course this article is quite out of date (though surprisingly much of it is still relevant) but on this point it was wrong even back when it was written.

sidebar & port table ?

ARJUN's picture

where is the sidebar & port table that were discussed in this article..?

links and sidebars

Keith Daniels's picture

This is not the magazine so there are no "sidebars" Check the links in the articles they have what you are looking for.

All the new OSs and windowing systems are oriented towards content consumption instead of content production.

--Steve Daniels 2013