Writing Device Drivers in Linux
Writing Device Drivers in Linux
Report
Computer A, Unix Applikationsprogrammering, 7.5
points
Abstract
The report will describe and give examples how to write a device driver
to the Linux operation system.
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
6 Conclusions / Discussion...................................................................16
Writing device drivers in Linux Table of Contents
Magnus Abrahamsson 2010-02-18
References........................................................................................................17
Terminology
Acronyms / Abbreviations
GUI Graphical User Interface.
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.
1.2 Outline
Chapter 2 describes brief the user space and kernel space.
Chapter 4 gives you more hands-on how to write a how to write a device
driver.
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
User processes
User service
Kernel
Hardware
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.
Table 1: Device driver events and their associated functions between kernel space and
user space.
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
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.
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.
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]
# 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.
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
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
# modinfo psmouse
description: PS/2 mouse driver
..
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
# mknod /dev/parportleds c 61 0
Remember also to set the right read/write user permissions on the file, for
example:
# make –C /usr/src/linux-headers-2.6.28-14-generic/
M={your modules dir path} modules
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
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.
To free the reserved memory for the module release_region() is used, which
takes the same arguments as check_region().
/* Reading port */
parportleds_buffer = inb(BASEPORT);
# cat /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
#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 */
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Magnus Abrahamsson<magnus@dof.se>");
MODULE_DESCRIPTION("Parallel port LED driver.");
/* Major number */
int parportleds_major = 61;
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);
fail:
parportleds_exit();
return result;
}
void parportleds_exit(void) {
/* Success */
return 0;
/* Success */
return 0;
}
/* Reading port */
parportleds_buffer = inb(BASEPORT);
char *tmp;
tmp=(char *)buf+count-1;
copy_from_user(&parportleds_buffer,tmp,1);
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.
References
[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
The electronic diagram below shows how to create the LED matrix that
you can use to monitor the parallel port with.
LEDs
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).
# cd /usr/src
# bunzip2 linux-source-2.6.x.tar.bz2
# tar xvf linux-source-2.6.x.tar.
# 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.
#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