Section 8.3. Driver Methods


8.3. Driver Methods

We've covered much ground in our short treatment of module utilities. In the remaining sections of this chapter, we describe the basic mechanism for communicating with a device driver from a user space program (your application code).

We have introduced the two fundamental methods responsible for one-time initialization and exit processing of the module. Recall from Listing 8-1 that these are module_init() and module_exit(). We discovered that these routines are invoked at the time the module is inserted into or removed from a running kernel. Now we need some methods to interface with our device driver from our application program. After all, two of the more important reasons we use device drivers are to isolate the user from the perils of writing code in kernel space and to present a unified method to communicate with hardware or kernel-level devices.

8.3.1. Driver File System Operations

After the device driver is loaded into a live kernel, the first action we must take is to prepare the driver for subsequent operations. The open() method is used for this purpose. After the driver has been opened, we need routines for reading and writing to the driver. A release() routine is provided to clean up after operations when complete (basically, a close call). Finally, a special system call is provided for nonstandard communication to the driver. This is called ioctl(). Listing 8-10 adds this infrastructure to our example device driver.

Listing 8-10. Adding File System Ops to Hello.c

#include <linux/module.h> #include <linux/fs.h> #define HELLO_MAJOR 234 static int debug_enable = 0; module_param(debug_enable, int, 0); MODULE_PARM_DESC(debug_enable, "Enable module debug mode."); struct file_operations hello_fops; static int hello_open(struct inode *inode, struct file *file) {     printk("hello_open: successful\n");     return 0; } static int hello_release(struct inode *inode, struct file *file) {     printk("hello_release: successful\n");     return 0; } static ssize_t hello_read(struct file *file, char *buf, size_t count,                 loff_t *ptr) {     printk("hello_read: returning zero bytes\n");     return 0; } static ssize_t hello_write(struct file *file, const char *buf,                 size_t count, loff_t * ppos) {     printk("hello_read: accepting zero bytes\n");     return 0; } static int hello_ioctl(struct inode *inode, struct file *file,                 unsigned int cmd, unsigned long arg) {     printk("hello_ioctl: cmd=%ld, arg=%ld\n", cmd, arg);     return 0; } static int __init hello_init(void) {     int ret;     printk("Hello Example Init - debug mode is %s\n",             debug_enable ? "enabled" : "disabled");     ret = register_chrdev(HELLO_MAJOR, "hello1", &hello_fops);         if (ret < 0) {             printk("Error registering hello device\n");             goto hello_fail1;         }     printk("Hello: registered module successfully!\n");     /* Init processing here... */     return 0; hello_fail1:     return ret; } static void __exit hello_exit(void) {     printk("Hello Example Exit\n"); } struct file_operations hello_fops = {     owner:   THIS_MODULE,     read:    hello_read,     write:   hello_write,     ioctl:   hello_ioctl,     open:    hello_open,     release: hello_release, }; module_init(hello_init); module_exit(hello_exit); MODULE_AUTHOR("Chris Hallinan"); MODULE_DESCRIPTION("Hello World Example"); MODULE_LICENSE("GPL");

This expanded device driver example includes many new lines. From the top, we've had to add a new kernel header file to get the definitions for the file system operations. We've also defined a major number for our device driver. (Note to device driver authors: This is not the proper way to allocate a device driver major number. Refer to the Linux kernel documentation (.../Documentation/devices.txt) or one of the excellent texts on device drivers for guidance on the allocation of major device numbers. For this simple example, we simply choose one that we know isn't in use on our system.)

Next we see definitions for four new functions, our open, close, read, and write methods. In keeping with good coding practices, we've adopted a consistent naming scheme that will not collide with any other subsystems in the kernel. Our new methods are called hello_open(), hello_release(), hello_read(), and hello_write(), respectively. For purposes of this simple exercise, they are do-nothing functions that simply print a message to the kernel log subsystem.

Notice that we've also added a new function call to our hello_init() routine. This line registers our device driver with the kernel. With that registration call, we pass a structure containing pointers to the required methods. The kernel uses this structure, of type struct file_operations, to bind our specific device functions with the appropriate requests from the file system. When an application opens a device represented by our device driver and requests a read() operation, the file system associates that generic read() request with our module's hello_read() function. The following sections examine this process in detail.

8.3.2. Device Nodes and mknod

To understand how an application binds its requests to a specific device represented by our device driver, we must understand the concept of a device node. A device node is a special file type in Linux that represents a device. Virtually all Linux distributions keep device nodes in a common location (specified by the Filesystem Hierarchy Standard [6]), in a directory called /dev. A dedicated utility is used to create a device node on a file system. This utility is called mknod.

[6] Reference to this standard is found in the "Suggestions for Additional Reading," at the end of this chapter.

An example of node creation is the best way to illustrate its functionality and the information it conveys. In keeping with our simple device driver example, let's create the proper device node to exercise it:

$ mknod /dev/hello1 c 234 0


After executing this command on our target embedded system, we end up with a new file called /dev/hello1 that represents our device driver module. If we list this file to the console, it looks like this:

$ ls -l /dev/hello1 crw-r--r--   1 root  root  234, 0 Jul 14 2005 /dev/hello1


The parameters we passed to mknod include the name, type, and major and minor numbers for our device driver. The name we chose, of course, was hello1. Because we are demonstrating the use of a character driver, we use c to indicate that. The major number is 234, the number we chose for this example, and the minor number is 0.

By itself, the device node is just another file on our file system. However, because of its special status as a device node, we use it to bind to an installed device driver. If an application process issues an open() system call with our device node as the path parameter, the kernel searches for a valid device driver registered with a major number that matches the device nodein our case, 234. This is the mechanism by which the kernel associates our particular device to the device node.

As most C programmers know, the open() system call, or any of its variants, returns a reference (file descriptor) that our applications use to issue subsequent file system operations, such as read, write, and close. This reference is then passed to the various file system operations, such as read, write, or their variants.

For those curious about the purpose of the minor number, it is a mechanism for handling multiple devices or subdevices with a single device driver. It is not used by the operating system; it is simply passed to the device driver. The device driver can use the minor number in any way it sees fit. As an example, with a multiport serial card, the major number would specify the driver. The minor number might specify one of the multiple ports handled by the same driver on the multiport card. Interested readers are encouraged to consult one of the excellent texts on device drivers for further details.



Embedded Linux Primer(c) A Practical Real-World Approach
Embedded Linux Primer: A Practical Real-World Approach
ISBN: 0131679848
EAN: 2147483647
Year: 2007
Pages: 167

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net