Professional Documents
Culture Documents
P.N. Anantharaman
Director Engineering, Adobe Systems India
http://www.freesoftwaremagazine.com/articles/drivers_linux
Part 3 - Examples
Device drivers are programs that allow applications to access the devices in a
consistent, uniform and safe manner
In Linux, the devices are treated like files and hence their access is very similar to the way
files are accessed, thus providing uniformity
The interfaces to integrate a new device driver in to the OS is well defined and consistent
across different devices. This allows third party developers to easily integrate their devices in
to the OS
Device drivers form part of the OS running in kernel mode that have access to kernel
data structures and functions
The device drivers hide these details from the application or rest of the operating system,
thus making it simple to access the device and also add/remove new devices
Making the device drivers part of kernel and disallowing applications to directly
manipulate devices have the following advantages:
User mode applications dont need to worry about specifics of device. Without the device
driver the applications are more complex to maintain.
Improved security as user mode applications are not allowed to manipulate devices directly
Without suitable device drivers the hardware is unusable and hence the
hardware vendors often supply the device drivers. Hence the device driver
development is very critical.
Suppose you are engaged in a project where you build a new hardware
device and are interested in integrating that with the computing system, you
often need to write your own driver
Suppose you are required to understand an existing device driver and need
to extend it for some other use, it is essential to understand the concept
and implementation of device drivers
Eg: A hard disk driver is policy free while the higher layers may decide who can
access the device, how the device storage is utilized as disk blocks etc
libraries
Buffer Cache
Character
Block Drivers
Drivers
Device Drivers
Hardware Control
2011 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.
Modules
Traditionally, the kernel is built as one kernel image having a fixed set of
functionalities. The device drivers were also built along with the kernel.
However, the newer versions of the kernel allow addition or removal of functionalities
to the kernel dynamically, when the kernel is already running
Each piece of code that can be added to the kernel at runtime can be introduced as
a module.
From the user space, one can install a module as a part of the kernel by using
insmod command and the module can be removed dynamically from the kernel by
using rmmod
Technically, a module is a piece of object code that can be linked with the kernel
dynamically. The module by itself is not an executable program
Modules are logically part of the kernel, they are not user programs though they may
be installed by user mode programs
Device drivers
File system driver (one for ext2, MSDOS FAT16, 32, NFS)
System calls
Network Drivers
Executable interpreters.
10
Modules (contd)
The module can then be compiled in to the object code (usually with .ko
extension) and installed in to the kernel
11
12
13
The __init macro causes the init function to be discarded and its memory
freed once the init function finishes for builtin drivers, but not loadable
modules.
__initdata works similarly to __init but for init variables rather than functions.
The __exit macro causes the omission of the function when the module is
built into the kernel, and has no effect for loadable modules. Again, if you
consider when the cleanup function runs, this makes complete sense;
builtin drivers don't need a cleanup function, while loadable modules do.
The kernel boot time message: Freeing unused kernel memory: 236k freed
is an illustration of this
14
Modules version 3
/* * hello3.c Illustrating the __init, __initdata and __exit macros. */
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
static int hello3_data __initdata = 3;
static int __init hello_3_init(void)
{
printk(KERN_INFO "Hello, world %d\n", hello3_data);
return 0;
}
static void __exit hello_3_exit(void)
{
printk(KERN_INFO "Goodbye, world 3\n");
}
module_init(hello_3_init);
module_exit(hello_3_exit);
2011 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.
15
Character drivers
A device can be accessed as a stream of bytes using file system calls like
open, read, write, close etc
Block drivers
Block drivers are meant for those devices that operate on blocks of data as
compared to the devices that work at a character level granularity
Usually, the data from/to these devices are buffered at driver level
Network Drivers
Network drivers operate on packets and need different handling at driver level
16
Hence the issue is: If we invoke a open file system call with
the device name as the file name, how does the kernel
locate the device and execute the right device driver
function? How does the kernel enforce permissions like the
way it enforces on regular files? How are the custom
functions (for example: take picture from camera) that may
be needed for a device are supported?
17
User Functions
Load Module
Open device
Read device
Write device
Close device
Remove module
18
Kernel Functions
Kernel Functions
Read data
Write data
19
20
obj-m += hello.o
21
User Functions
Kernel Functions
Load Module
insmod
module_init()
rmmod
module_exit()
Open device
Read device
Write device
Close device
Remove module
22
23
Memory.c
MODULE_LICENSE("Dual BSD/GPL");
/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);
/* Structure that declares the usual file */ /* access functions */
struct file_operations memory_fops = {
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
/* Declaration of the init and exit functions */ module_init(memory_init); module_exit(memory_exit);
/* Global variables of the driver Major number */ int memory_major = 60;
/* Buffer to store data */ char *memory_buffer;
2011 Adobe Systems Incorporated. All Rights Reserved. Adobe Confidential.
24
Hence it is necessary to create a node for the device in the file system
In the above example, we created a device file by name memory that can
be accessed by user mode program referring to this as /dev/memory.
This is a character device with a major number 4 and minor number 0
How does the device file name is associated with the right device driver
in the kernel? The register_chrdev function makes this connection. It is
called with three arguments: major number, a string of characters
showing the module name, and a file_operations structure which links the
call with the file functions it defines. It is invoked, when installing the
module, in this way:
25
26
27
28
29
User mode applications typically use a library function fopen to open a file. This call
maps to the member function open of the file_operations structure.
The file_operations strucutre for a device driver is registered with the kernel through
a call to register_chrdev function
The structure associates a function pointer with the member field open of the
variable of form file_operations structure. In this case, it is the memory_open
function. It takes as arguments: an inode structure, which sends information to the
kernel regarding the major number and minor number; and a file structure with
information relative to the different operations that can be performed on a file.
When a file is opened, its normally necessary to initialize driver variables or reset
the device. In this simple example, though, these operations are not performed.
30
User Functions
Kernel Functions
Load Module
insmod
module_init()
Open device
fopen
file_operations: open
rmmod
module_exit()
Read device
Write device
Close device
Remove module
31
File Close
In our example, we dont need to do anything special for closing the device
32
User Functions
Kernel Functions
Load Module
insmod
module_init()
Open device
fopen
file_operations: open
Close device
fclose
file_operations: release
Remove module
rmmod
module_exit()
Read device
Write device
33
The user space function fread or fscanf etc use the member read of the
file_operations structure. We will map the driver function memory_read to
the member function read and hence memory_read will be invoked
whenever fread or fscanf is called with the file descriptor that corresponds
to our /dev/memory
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
/* Transfering data to user space */
copy_to_user(buf,memory_buffer,1);
/* Changing reading position as best suits */
if (*f_pos == 0) { *f_pos+=1; return 1; } else return 0;
}
34
User Functions
Kernel Functions
Load Module
insmod
module_init()
Open device
fopen
file_operations: open
Read device
fread
file_operations: read
Close device
fclose
file_operations: release
Remove module
rmmod
module_exit()
Write device
35
The user space function fwrite or fprintf etc use the member write of the
file_operations structure. We will map the driver function memory_write to
the member function write and hence memory_write will be invoked
whenever fwrite or fprintf is called with the file descriptor that corresponds
to our /dev/memory
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
char *tmp;
tmp=buf+count-1;
copy_from_user(memory_buffer,tmp,1);
return 1;
}
36
User Functions
Kernel Functions
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 module
rmmod
module_exit()
37
38
1.
2.
3.
4.
5.
6.
7.
39