You are on page 1of 26

Writing device drivers in Linux Table of Contents

Magnus Abrahamsson 2010-02-18

Mid Sweden University


The Department of Information Technology and Media (ITM)
Author: Magnus Abrahamsson
E-mail address: mrabris@gmail.com
Scope: 6630 words inclusive of appendices
Date: 2010-02-18
Writing device drivers in Linux Table of Contents
Magnus Abrahamsson 2010-02-18

Report
Computer A, Unix Applikationsprogrammering, 7.5
points

Writing device drivers in Linux


LED Parallel port
Magnus Abrahamsson
Writing device drivers in Linux Table of Contents
Magnus Abrahamsson 2010-02-18

Abstract
The report will describe and give examples how to write a device driver
to the Linux operation system.

Keywords: Device driver, Linux, Ubuntu, Modules, Kernel, C.


Writing device drivers in Linux Table of Contents
Magnus Abrahamsson 2010-02-18

Foreword
This report is written as an “exam” work to complete my Unix Applika-
tionsutveckling course, that I begun in another millennium. So instead of
taking a new cause exam my teacher Mikael Hasselman suggested that I
write a report on how to create a device driver for Linux. Even if it’s a long
time since I took the course I’m pretty sure we didn’t spend that much
time in kernel space back then… So this has been quit a challenge for
me, but a good one. I hope you enjoy reading it as much as I liked
writing and working with it.

Kind regards

Magnus Abrahamsson
Writing device drivers in Linux Table of Contents
Magnus Abrahamsson 2010-02-18

Table of Contents
Abstract ............................................................................................................iii

Foreword .......................................................................................................... iv

Terminology................................................................................................... vii

1 Introduction............................................................................................1
1.1 Scope .............................................................................................1
1.2 Outline ..........................................................................................1
1.3 Contributions ...............................................................................1

2 Introduction to Kernel and User space..............................................2


2.1 Kernel space .................................................................................2
2.2 User space.....................................................................................2
2.3 Interfacing functions between user space and kernel space .3
2.4 Interfacing functions between kernel space and the
hardware device. .........................................................................3
2.5 Linux System Calls......................................................................4

3 Devices in Linux ....................................................................................5


3.1 Devices Nodes – Special files.....................................................5
3.1.1 Character and block devices ...........................................5
3.2 Device Driver ...............................................................................6
3.3 Loadable Modules.......................................................................7

4 How to write a device driver ...............................................................9


4.1 Connection of the device with its files......................................9
4.2 Load and removing driver.........................................................9
4.2.1 Load and remove modules in user space......................9
4.2.2 Initializing modules in kernel space ............................10
4.2.3 Removing modules in kernel space .............................11
4.3 Read and write to the device ...................................................12
4.3.1 Reading the device in kernel space..............................12
4.3.2 Reading the device from user space ............................12
4.3.3 Writing to the device in kernel space ..........................12
4.3.4 Writing to the device from user space.........................12

5 LED parallel port device driver - example code. ...........................13

6 Conclusions / Discussion...................................................................16
Writing device drivers in Linux Table of Contents
Magnus Abrahamsson 2010-02-18

References........................................................................................................17

Appendix A: The LED Matrix......................................................................18

Appendix B: Compile the kernel on Ubuntu system ..............................19


Writing device drivers in Linux Terminology
Magnus Abrahamsson 2010-02-18

Terminology

Acronyms / Abbreviations
GUI Graphical User Interface.

LED Light Emitting Diode

I/O Input / Output

PCMCIA Personal Computer Memory Card Interna-


tional Association, also called PC-Card

Code notation
Symbol Description

Function() Functions
Writing device drivers in Linux 1 Introduction
Magnus Abrahamsson 2010-02-18

1 Introduction
I will in this report briefly describe how to write a device driver for Linux
and give one example how to create a parallel port LED driver.

1.1 Scope
There are several different devices for the Linux operation system. This
study has its focus on type char devices loaded as modules on an i386.

I have also chosen not to use the platform_driver driver model, introduced in
kernel 2.6.15, because I wanted to get “direct” connection with the kernel.
So in that why I’m not following the standard driver model convention,
where discovery/enumeration is handled outside the drivers, and drivers
provide probe() and remove() methods.

How to access hardware memory has also been outlined.

1.2 Outline
Chapter 2 describes brief the user space and kernel space.

Chapter 3 gives you more information regarding devices

Chapter 4 gives you more hands-on how to write a how to write a device
driver.

Chapter 5 gives you an example of a device driver written in C.

Chapter 6 gives you conclusions and more comments on the subject.

1.3 Contributions
Special big thanks to my colleague Stellan Blanc which have been very
supportive during the whole process.
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

2 Introduction to Kernel and User space.


When you write a device driver for Linux it’s impotent to make the distinc-
tion between “user space” and kernel space”

2.1 Kernel space


The kernel manages the machine’s hardware in a simple and efficient man-
ner, offering user a simple and uniform programming interface. In that
way, the kernel, and in particular its device driver, form a bridge or inter-
face between and end-user/programmer and the hardware. Any subrou-
tines or functions forming part of the kernel (modules and device drivers, for
example) are considered to be part of the kernel space.

2.2 User space


This is where the end-user programs reside. UNIX shell or other GUI based
applications (gnome-system-monitor) are a part of the user space, for example.
When these applications need to interact with the system’s hardware they
don’t do so directly. They go through the kernel supported functions,
shown in figure 1

User processes
User service
Kernel

Hardware

Figure 1 : Linux architecture


2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

2.3 Interfacing functions between user space and kernel space


The kernel offers several subroutines or functions in user space. The end-
user application programmer is allowed through these functions to interact
with the hardware from user space. In a UNIX or Linux system, this dia-
logue is preformed via the system call interface. Usually, these operations
are read and write file functions. The reason for this is that in UNIX devices
are seen, from the user’s point of view, as files.

Usually, for each function in the user space (allowing the use of devices or
files), there exists an equivalent in kernel space (allowing the transfer of
information from kernel to the user and vice-versa). See Table 1.

Event User function Kernel function


Load module insmod module_init()
Open device fopen file_operations:open
Read device fread file_operations:read
Write device fwrite file_operations:write
Close device fclose file_operations:release
Remove device rmmod module_exit()

Table 1: Device driver events and their associated functions between kernel space and
user space.

2.4 Interfacing functions between kernel space and the


hardware device.
As we mentioned in the earlier there are also functions in kernel space
which control the device or exchange information between the kernel and
the hardware (kernel functions). See Table 2.

Events Kernel function


Read data inb()
Write data outb()

Table 2: Device driver events and their associated functions between kernel space and
hardware device.
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

User Applications
User
Space

Libraries

System call interface

File Subsystem Process Control Subsystem

Memory Inter-process
Scheduler
Managment Communication
Buffer Cache Kernel
Space

Character Block

Device Driver

Hardware Control

Hardware

Linux Platform

Figure 2: Shows that user space programs communicate with the kernel using system
calls.

2.5 Linux System Calls


A system call is an interface between a user-space application and a service
that the kernel provides. Because the service is provided in the kernel, a
direct call cannot be performed; instead, you must use a process of crossing
the user-space/kernel boundary. The way you do this differs based on the
particular architecture. In this document we will stick to the most common
architecture, i386.
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

3 Devices in Linux
We will in this chapter look deeper into the different definitions as device
nodes, device drivers and loadable modules in a Linux system.

3.1 Devices Nodes – Special files


All devices look like files on a Linux system. In fact, the user-level interface
to a device is called special file. These special files or device nodes reside in
the /dev directory. Special files, like other files, have read and write
permissions.

To link the special files with the kernel module two numbers are used:
major number and minor number. Major device numbers are used by the
Linux system to map I/O requests to the driver code, thereby deciding
which device driver to execute, when a user reads from or writes to the
special file. The minor numbers are entirely under the control of the driver
writer, and usually refer to sub-devices of the device. These sub-devices
may be separate units attached to a controller. Thus, a disk device driver
may, for example, communicate with a hardware controller (the device)
which has several disk drives (sub-devices) attached. [4]

In the /usr/src/linux/Documentation/devices.txt file you can find the official


registry of allocated device numbers for the Linux operating system.

3.1.1 Character and block devices


There are two kinds of devices: character devices and block devices. They differ
in how they transfer data between the device and the computer memory.

Character device: transfers data character by character. A character device


usually produces a stream of characters (like a keyboard), consumes a
stream of characters (like a printer), or does both (like a terminal or
modem).
Block device: transfer data in batches of characters called blocks. A disk
drive is an example of a block device, whereas, terminals and line printers
are examples of character devices. This report will only handle character
device from now on. [3]
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

The following command can be used to yield the status information of an


existing device node:

# ls -l /dev/lp*
crw-rw-rw 1 root root 6, 0 April 23 1994 /dev/lp0

This example indicates that: lp0 is a character type device (the first letter of
the file mode field is c), the major number is 6, and minor device number 0
is assigned to the device.

How to create a character device file and connect it with the device driver
will be handle later in chapter 4.

3.2 Device Driver


The device driver is sometimes called an interface to its device. It is a
collection of subroutines and data within the kernel that constitutes the
software interface to an I/O device. When the kernel recognizes that a
particular action is required from the device, it calls the appropriate driver
routine, which passes control from the user process to the driver routine.
Control is returned to the user process when the driver routine has
completed. (fig 3.) [5]

Figure 3: Shows the relationship between device driver and the Linux system
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

A device driver provides the following features:


• A set of routines that communicate with a hardware device and
provide a uniform interface to the operating system kernel.
• A self-contained component that can be added to, or removed from,
the operating system dynamically (modules).
• Management of data flow and control between user programs and a
peripheral device.
• A user-defined section of the kernel that allows a program or a
peripheral device to appear as a ”/dev“ device to the rest of the
system's software.

3.3 Loadable Modules


When you install Linux, the kernel is automatically configured to run. Many
assumptions are building into this kernel, including the type of driver need
to run the hardware and services that the kernel provides. There are times
when you have to change these assumptions. That could be done by recon-
figure the kernel. But not all changes to the features in the kernel require a
rebuild. Many drivers are available to an installed Linux system in the form
of loadable modules. Loadable modules can be used to add features to the
running kernel from user space. For example the PCMCIA feature uses
loadable modules. [2]

List modules
To see which modules are currently loaded into the running kernel space
from user space the lsmod command can be used. Here is an example:

# lsmod
Module Size Used by
parportleds 10372 0
i915 65668 2
binfmt_misc 16776 1
drm 96424 3 i915
bridge 56212 0
stp 10500 1 bridge
bnep 20224 2
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

Information about a module


To get more information about the loaded modules, you can use the modinfo
command. For example:

# modinfo psmouse
description: PS/2 mouse driver
..

Not all modules have descriptions available.

A module can’t accomplish its task without using system resources such as
memory, I/O ports, I/O memory, and interrupt lines. We will look at the I/O
port registration, because that’s what our parallel port example will use
later on.
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

4 How to write a device driver


In this chapter you have a brief tutorial how to write a device driver for
Linux.

4.1 Connection of the device with its files


If a device file doesn’t exist you have to create it. A new device file could be
created by typing the following command as root:

# mknod /dev/parportleds c 61 0

In the example above, c means that a char device is to be created, 61 is the


major number and 0 is the minor number.

Remember also to set the right read/write user permissions on the file, for
example:

# chmod 666 /dev/parportleds

4.2 Load and removing driver


Since the release of kernel version 2.6.x compiling modules has become
slightly more complicated. First, you need to have a complete, compiled
kernel source-code-tree. It’s now also necessary to compile the module
using the same kernel that you’re going to load and use the module with.
[Appendix B]

The modules are compiled with the following command:

# make –C /usr/src/linux-headers-2.6.28-14-generic/
M={your modules dir path} modules

4.2.1 Load and remove modules in user space

Load modules
To load a module into the kernel use the following command as root:

# insmod parportleds.ko
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

You could also use the more sophisticated modprobe command to load
modules. The .ko file then has to be moved to /lib/modules to be loaded. The
big options with modprobe are that it adds modules, remove modules, and
find module dependencies.

Remove modules
To remove a module from the kernel use following command as root:

# rmmod parportleds

4.2.2 Initializing modules in kernel space


In order to load and remove a driver in kernel space, parameters are passed
to the module_init() and module_exit() function.

To initialize the module to the right I/O system resource module_init()


function needs modification.

We star with link the char driver with the corresponding /dev file in kernel
space by using the register_chardev() function. It is called with the argument:
major number, a string of characters showing the module name, and a
file_operations structure which links the call with the file function it
defines.

int result
/* Registering device */
result = register_chrdev(61,"parportleds",
&parportleds_fops);

When this is done you need to know what memory address the system
resource are using. In our case, for the parallel port it’s 0x0378. So when
you know the address you should check if the memory region is available
(check_region), and then reserve it with request_region().
Both functions have as argument the base address of the memory region
and its length. The request region function also has third argument, device
name, which connection the memory address with the device name. [4]

/* Registering port */
port = check_region(0x378, 1);
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

if (port) {
printk("<1>parportleds: cannot reserve 0x378\n");
result = port;
goto fail;
}
request_region(0x378, 1, "parportleds");

When you have loaded a module into kernel space you should see this in
ioports by typing:

# cat /proc/ioports
..
0378-0378 : parportleds
..

Each entry in the file specifies (in hexadecimal) a range of ports locked by a
driver or owned by a hardware device.

You can also se the printk() function above, it’s very similar to the well
known printf(), but it only works inside the kernel. The printk() writes to
the kernel system log file (/var/log/syslog), and you should also receive this
message in the system console. The <1> symbol shows the priority of the
message. The lower number the high prio.

4.2.3 Removing modules in kernel space


To remove a module from the kernel space the module_exit() function needs
some modification.

The major number is released from the module by using the


unregister_chrdev() function.

/* Make major number free! */


unregister_chrdev(parportleds_major, DRVNAME);

To free the reserved memory for the module release_region() is used, which
takes the same arguments as check_region().

/* Make port free! */


release_region(BASEPORT,1);
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

4.3 Read and write to the device


4.3.1 Reading the device in kernel space
The read from the device function is called inb(). It returns the information
to the user space by taking the base port address (0x378) as in argument and
returns the content of the port.

/* Reading port */
parportleds_buffer = inb(BASEPORT);

4.3.2 Reading the device from user space


To read the stat of the parallel port from user space you could use the
following command:

# cat /dev/parportleds

4.3.3 Writing to the device in kernel space


The write to device function is called outb(). It makes it possible to transfer
data from user space to a device by taking the content to write in the port
and its address.

/* Writing to the port */


outb(parportleds_buffer,BASEPORT);

4.3.4 Writing to the device from user space


To write to the parallel port from user space and in that way turn on the
LEDs, execute the command:

# echo –n W > /dev/parportleds

This should turn on LED 1 to 3, 5 and 7. Leaving all of the others turned off.
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

5 LED parallel port device driver -


example code.
/********************************************************/
/* C header */
/* Filname: parportleds.c */
/* Created: 2009-08-05 */
/* Author: Magnus Abrahamsson */
/* Email: magnus.abrahamsson@teliasonera.com */
/* */
/* -----------CHANGE HISTOR------------------ */
/* Date Done by Descriptipon */
/* ------------------------------------------ */
/* 05-AUG-2009 Magnus A Created */
/* 15-SEP-2009 Magnus A Module info added */
/* ------------------------------------------ */
/* */
/* Description: LED Parallel port device driver */
/* */
/* */
/********************************************************/

#include <linux/init.h>
#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/ioport.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
#include <asm/io.h> /* inb, outb */

#define DRVNAME "parportleds"


#define BASEPORT 0x378

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Magnus Abrahamsson<magnus@dof.se>");
MODULE_DESCRIPTION("Parallel port LED driver.");

/* Function declaration of parportleds.c */


int parportleds_open(struct inode *inode, struct file *filp);
int parportleds_release(struct inode *inode, struct file *filp);
ssize_t parportleds_read(struct file *filp, char *buf,
size_t count, loff_t *f_pos);
ssize_t parportleds_write(struct file *filp, const char *buf,
size_t count, loff_t *f_pos);
void parportleds_exit(void);
int parportleds_init(void);

/* Structure that declares the common */


/* file access fcuntions */
struct file_operations parportleds_fops = {
read: parportleds_read,
write: parportleds_write,
open: parportleds_open,
release: parportleds_release
};

/* Driver global variables */


2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

/* Major number */
int parportleds_major = 61;

/* Control variable for memory */


/* reservation of the parallel port*/
int port;

module_init(parportleds_init);
module_exit(parportleds_exit);

int parportleds_init(void) {
int result;

/* Registering device */
result = register_chrdev(parportleds_major, DRVNAME,
&parportleds_fops);
if (result < 0) {
printk(
"<1>parport: cannot obtain major number %d\n",
parportleds_major);
return result;
}

/* Registering port */
port = check_region(BASEPORT, 1);
if (port) {
printk("<1>parportleds: cannot reserve 0x378 \n");
result = port;
goto fail;
}
request_region(BASEPORT, 1, DRVNAME);

printk("<1>Inserting parportled module\n");


return 0;

fail:
parportleds_exit();
return result;
}

void parportleds_exit(void) {

/* Make major number free! */


unregister_chrdev(parportleds_major, DRVNAME);

/* Make port free! */


if (!port) {
release_region(BASEPORT,1);
}

printk("<1>Removing module parportleds\n");


}

int parportleds_open(struct inode *inode, struct file *filp) {

/* Success */
return 0;

int parportleds_release(struct inode *inode, struct file *filp) {

/* Success */
return 0;
}

ssize_t parportleds_read(struct file *filp, char *buf,


size_t count, loff_t *f_pos) {

/* Buffer to read the device */


char parportleds_buffer;

/* Reading port */
parportleds_buffer = inb(BASEPORT);

/* We transfer data to user space */


copy_to_user(buf,&parportleds_buffer,1);
2 Introduction to Kernel and User
Writing device drivers in Linux space.
Magnus Abrahamsson 2010-02-18

/* We change the reading position as best suits */


if (*f_pos == 0) {
*f_pos+=1;
return 1;
} else {
return 0;
}
}

ssize_t parportleds_write( struct file *filp, const char *buf,


size_t count, loff_t *f_pos) {

char *tmp;

/* Buffer writing to the device */


char parportleds_buffer;

tmp=(char *)buf+count-1;
copy_from_user(&parportleds_buffer,tmp,1);

/* Writing to the port */


outb(parportleds_buffer,BASEPORT);
printk(
"<1>parport write: %d\n",
parportleds_buffer);
return 1;
}
Writing device drivers in Linux 6 Conclusions / Discussion
Magnus Abrahamsson 2010-02-18

6 Conclusions / Discussion
Before you load the parportleds device driver you have to remove the exist-
ing parallel loadable modules (for example, lp, parport, parport_cp, ppdev)
from the kernel, to be get hold of the parallel port memory area.

In my case rmmod wasn’t enough, so I had to remove/move parport from


/lib/modules and reboot the system with out it.
Writing device drivers in - Error! References
Reference source not found. 2010-02-18
Magnus Abrahamsson

References

[1] Michael K. Johnson; LINUX Kernel Hackers' Guide ; 201 Howell


Street, Apt. 1C, Chapel Hill, North Carolina 27514-4818, 1993.

[2] Christopher Negus; Red Hat Linux Bible 7.2. ISBN 0-7645-3630-3,
2002, s 66

[3] Paul W. Abrahams, Bruce R. Larson; UNIX for the impatient, 2nd
edition. ISBN 0-201-41979-3, s86-91

[4] Alessandro Rubini & Jonathan Corbet; Linux Device Drivers, 2nd
Edition. ISBN 0-59600-008-1, 586 pages, June 2001

[5] By Tom Coffey and Andrew O’Shaughnessy; Networkcomput-


ing.com,”Write a Linux Hardware device driver”,
http://www.networkcomputing.com/unixworld/tutorial/010/010.t
xt.html

[6] IBM, ”Technical library”,


http://www.ibm.com/developerworks/views/linux/library.jsp
Published 07 Nov 2006. Retreived 2009-09-10.

Front page Illustration: Linux Kernel, By James M. Kenefick Jr. based on


work by Rusty Russell and Christian Reinger
Writing device drivers in - Error!
Reference source not found. Appendix A: The LED Matrix
Magnus Abrahamsson 2010-02-18

Appendix A: The LED Matrix

The electronic diagram below shows how to create the LED matrix that
you can use to monitor the parallel port with.

LEDs

~500 Ohm 25 pin D-SUB male connector

Pin #: 9 8 7 6 5 4 3 2 25

Bit #: 7 6 5 4 3 2 1 0

Figure 3: The electronic diagram for the LED matix and how it’s connected to the
D-25 male.

Figure 4: The 25-pin D-SUB male and the LED matrix showing a “W” (ASCII-87,
B:11101010).

WARNING! Connecting devices to the parallel port can harm your


computer. Make sure that you are properly earthed and you computer
is turned of when connecting the device. Any problems that arise due
to undertaking these experiments are your sole responsibility.
Writing device drivers in - Error! Appendix B: Compile the kernel
Reference source not found. on Ubuntu system
Magnus Abrahamsson 2010-02-18

Appendix B: Compile the kernel on


Ubuntu system
1. Install the kernel-image-2.6.x package:

# apt-get install linux-image

2. Install the kernel-source-2.6.x package

# apt-get install linux-source

3. Change to source code directory:

# cd /usr/src

4. Unzip and untar the source code:

# bunzip2 linux-source-2.6.x.tar.bz2
# tar xvf linux-source-2.6.x.tar.

5. Change to kernel source directory:

# cd /usr/src/linux-source-2.6.x

6. Copy the default Linux kernel configuration file to your local kernel
source directory

# cp /boot/config-2.6.x .config.

7. Make the kernel and the modules with

#make
#make modules
(take a break..)

8. Fix grub.
# vi /etc/grub.conf
title Ubuntu Core (2.6.x-generic)
root (hd0,0)
kernei /boot/vmlinuz-2.6.x-generic ro root=/dev/sda3
initrd /boot/initrd.img-2.6.x-generic

You might also like