You are on page 1of 26

Writing device drivers in Linux Magnus Abrahamsson

Table of Contents 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 Magnus Abrahamsson

Table of Contents 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 Magnus Abrahamsson

Table of Contents 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 Magnus Abrahamsson

Table of Contents 2010-02-18

Foreword
This report is written as an exam work to complete my Unix Applikationsutveckling 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 its a long time since I took the course Im pretty sure we didnt 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 Magnus Abrahamsson

Table of Contents 2010-02-18

Table of Contents
Abstract ............................................................................................................iii Foreword .......................................................................................................... iv Terminology................................................................................................... vii 1 1.1 1.2 1.3 2 2.1 2.2 2.3 2.4 2.5 Introduction............................................................................................1 Scope .............................................................................................1 Outline ..........................................................................................1 Contributions ...............................................................................1 Introduction to Kernel and User space..............................................2 Kernel space .................................................................................2 User space.....................................................................................2 Interfacing functions between user space and kernel space .3 Interfacing functions between kernel space and the hardware device. .........................................................................3 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 6 LED parallel port device driver - example code. ...........................13 Conclusions / Discussion...................................................................16

Writing device drivers in Linux Magnus Abrahamsson

Table of Contents 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 Magnus Abrahamsson

Terminology 2010-02-18

Terminology
Acronyms / Abbreviations
GUI LED I/O PCMCIA Graphical User Interface. Light Emitting Diode Input / Output Personal Computer Memory Card International Association, also called PC-Card

Code notation
Symbol Function() Description Functions

Writing device drivers in Linux Magnus Abrahamsson

1 Introduction 2010-02-18

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 Im 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.

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 2010-02-18

Introduction to Kernel and User space.


When you write a device driver for Linux its impotent to make the distinction between user space and kernel space

2.1

Kernel space
The kernel manages the machines hardware in a simple and efficient manner, offering user a simple and uniform programming interface. In that way, the kernel, and in particular its device driver, form a bridge or interface between and end-user/programmer and the hardware. Any subroutines 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 systems hardware they dont do so directly. They go through the kernel supported functions, shown in figure 1

User processes User service Kernel

Hardware

Figure 1 : Linux architecture

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 2010-02-18

2.3

Interfacing functions between user space and kernel space


The kernel offers several subroutines or functions in user space. The enduser application programmer is allowed through these functions to interact with the hardware from user space. In a UNIX or Linux system, this dialogue 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 users 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 Load module Open device Read device Write device Close device Remove device User function insmod fopen fread fwrite fclose rmmod Kernel function module_init() file_operations:open file_operations:read file_operations:write file_operations:release 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 Read data Write data Kernel function inb() outb()

Table 2: Device driver events and their associated functions between kernel space and hardware device.

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 2010-02-18

User Applications
User Space

Libraries

System call interface

File Subsystem

Process Control Subsystem Scheduler Memory Managment Inter-process Communication Kernel Space

Buffer Cache

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.

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 2010-02-18

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]

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 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

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 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 reconfigure 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 parportleds i915 binfmt_misc drm bridge stp bnep Size 10372 65668 16776 96424 56212 10500 20224 Used by 0 2 1 3 i915 0 1 bridge 2

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 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 cant 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 thats what our parallel port example will use later on.

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 2010-02-18

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 doesnt 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. Its now also necessary to compile the module using the same kernel that youre 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

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 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 its 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);

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 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, its 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);

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 2010-02-18

4.3
4.3.1

Read and write to the device


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.

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 2010-02-18

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

Writing device drivers in Linux Magnus Abrahamsson


/* 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;

2 Introduction to Kernel and User space. 2010-02-18

/* 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);

Writing device drivers in Linux Magnus Abrahamsson

2 Introduction to Kernel and User space. 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 Magnus Abrahamsson

6 Conclusions / Discussion 2010-02-18

Conclusions / Discussion
Before you load the parportleds device driver you have to remove the existing 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 wasnt enough, so I had to remove/move parport from /lib/modules and reboot the system with out it.

Writing device drivers in - Error! Reference source not found. Magnus Abrahamsson

References 2010-02-18

References
[1] Michael K. Johnson; LINUX Kernel Hackers' Guide ; 201 Howell Street, Apt. 1C, Chapel Hill, North Carolina 27514-4818, 1993. Christopher Negus; Red Hat Linux Bible 7.2. ISBN 0-7645-3630-3, 2002, s 66 Paul W. Abrahams, Bruce R. Larson; UNIX for the impatient, 2nd edition. ISBN 0-201-41979-3, s86-91 Alessandro Rubini & Jonathan Corbet; Linux Device Drivers, 2nd Edition. ISBN 0-59600-008-1, 586 pages, June 2001 By Tom Coffey and Andrew OShaughnessy; Networkcomputing.com,Write a Linux Hardware device driver, http://www.networkcomputing.com/unixworld/tutorial/010/010.t xt.html IBM, Technical library, http://www.ibm.com/developerworks/views/linux/library.jsp Published 07 Nov 2006. Retreived 2009-09-10.

[2]

[3]

[4]

[5]

[6]

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. Magnus Abrahamsson

Appendix A: The LED Matrix 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 #:

25

Bit #:

Figure 3: The electronic diagram for the LED matix and how its 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! Reference source not found. Magnus Abrahamsson

Appendix B: Compile the kernel on Ubuntu system 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