utilities and do not affect the behavior of the device driver itself. You can learn more about modinfo from its man page and the modinfo source itself.
One very useful feature of modinfo is to learn what parameters the module supports. From Listing 8-9, you can see that this module supports just one parameter. This was the one we added in Listing 8-6, debug_enable. The listing gives the name, type (in this case, an int), and descriptive text field we entered with the MODULE_PARM_DESC() macro. This can be very handy, especially for modules in which you might not have easy access to the source code.
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
');
return 0;
}
static int hello_release(struct inode *inode, struct file *file) {
printk('hello_release: successful
');
return 0;
}
static ssize_t hello_read(struct file *file, char *buf, size_t count, loff_t *ptr) {
printk('hello_read: returning zero bytes
');
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
');
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
', cmd, arg);
return 0;
}
static int __init hello_init(void) {
int ret;
printk('Hello Example Init - debug mode is %s
', debug_enable ? 'enabled' : 'disabled');
ret = register_chrdev(HELLO_MAJOR, 'hello1', &hello_fops);
if (ret < 0) {
printk('Error registering hello device
');
goto hello_fail1;
}
printk('Hello: registered module successfully!
');
/* Init processing here... */
return 0;
hello_fail1:
return ret;
}
static void __exit hello_exit(void) {
printk('Hello Example Exit
');
}
struct file_operations hello_fops = {
owner: THIS_MODULE,
read: hello_read,
write: hello_write,
ioctl: hello_ioctl,
open: hello_open,
release: hello_release,