Professional Documents
Culture Documents
Writing Devive Drivers in Linux
Writing Devive Drivers in Linux
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
Magnus Abrahamsson
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 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.
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
References........................................................................................................17 Appendix A: The LED Matrix......................................................................18 Appendix B: Compile the kernel on Ubuntu system ..............................19
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
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.
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
Hardware
2.3
Table 1: Device driver events and their associated functions between kernel space and user space.
2.4
Table 2: Device driver events and their associated functions between kernel space and hardware device.
User Applications
User Space
Libraries
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
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
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]
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
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
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.
4.1
4.2
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
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);
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);
4.3
4.3.1
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.
#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 */
/* 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);
/* 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; }
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]
Writing device drivers in - Error! Reference source not found. Magnus Abrahamsson
LEDs
~500 Ohm
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