Dynamic Kernels: Discovery

This article, the second of five, introduces part of the actual code to create custom module implementing a character device driver. It describes the code for module initialization and cleanup, as well as the open and close system calls.
Autodetecting the Device

init_module() calls the function skel_find() to perform the dirty task of detecting if a board is there. The function is very device specific, because each device must be probed for its peculiar features; thus, I won't try to show code to perform the actual probing, but only IRQ autodetection.

Unfortunately, some peripherals can't tell which IRQ line they're configured to use, thus forcing the user to write the IRQ number on the command line of insmod, or to hardcode the number in the software itself. Both these approaches are bad practice, because you just can't plug the board (after setting the jumpers) and load the driver. The only way to autodetect the IRQ line for these devices is a trial-and-error technique, which is, of course, only viable if the hardware can be instructed to generate interrupts.

The code in Listing 2 shows skel_find(), complete with IRQ autodetection. Some details of IRQ handling may appear obscure to some readers, but they will be clarified in the next article. To summarize, this code cycles through each of the possible IRQ lines, asking to install a handler, and looks to see if interrupts are actually generated by the board.

The field hwirq in the hardware structure represents the useable interrupt line, while the field irq is only valid when the line is active (after request_irq()). As explained in the last issue, it makes no sense to keep hold of an IRQ line when the device is not in use; that's why two fields are used.

Please note that I wrote this code as a work-around for the limitations of one of my hardware boards; if your hardware is able to report the IRQ line it's going to use, it's much better to use that information instead. The code is quite stable, anyway, if you are able to tailor it to your actual hardware. Fortunately, most good hardware is able to report its own configuration.

fops and filp

After the module has been loaded and the hardware has been detected, we must see how the device is acted upon. This means introducing the role of fops and filp: these little beasts are the most important data structures—actually, variable names—used in interfacing the device driver with the kernel.

fops is the name usually devoted to a struct file_operations. The structure is a jump table (structure of pointers to functions), and each field refers to one of the different operations performed on a filesystem node (open(), read(), ioctl(), etc.).

A pointer to your own fops is passed to the kernel by means of register_chrdev(), so that your functions will be called whenever one of your nodes is acted upon. We already wrote that line of code, but didn't show the actual fops. Here it is:

struct file_operations skel_fops {
  NULL,       /* skel_readdir */

Each NULL entry in your fops means that you're not going to offer that functionality for your device (select is special, in this respect, but I won't expand on it), each non-NULL entry must be a pointer to a function implementing the operation for your device. Actually, there exist a few more fields in the structure, but our example will live with the default NULL value (the C compiler fills up an incomplete structure with zero bytes without issuing any warning). If you are really interested in them, you can look at the structure's definition in <linux/fs.h>. filp is the name usually devoted to one of the arguments passed by the kernel to any function in your fops, namely a struct file *. The file structure is used to keep all the available status information about an “open file”, beginning with a call to open() and up to a call to close(). If the device is opened multiple times, different filps will be used for each instance: this means that you'll need to use your own data structure to keep hardware information about your devices. The code fragments within this installment already use an array of Skel_Hw, to hold information about several boards installed on the same computer. What is missing, then, is a way to embed hardware information in the file structure, in order to instruct the driver to operate on the right device. The field private_data exists in struct file just for that task, and is a pointer to void. You'll make private_data point to your hardware information structure when skel_open() gets invoked. If you need to keep some extra information private to each filp (for example, if two device nodes access the same hardware in two different ways), then you'll need a specific structure for private_data, which must be kmalloc()ed on open and kfree()ed on close. The implementations of open() and close() that we'll see later, work in this way.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Where are functions defined

Anonymous's picture


In which file are all the functions which are declared in the struct file_operations e.g open(), read();


Wherever You Like

Mitch Frazier's picture

Those functions have to be supplied by you. So you can put them wherever seems most appropriate for your code.

Mitch Frazier is an Associate Editor for Linux Journal.