Difference between revisions of "EBC Exercise 26 Device Drivers Details"
m (→Module Build Infrastructure: Formatting) |
m (→Module Build Infrastructure) |
||
| Line 47: | Line 47: | ||
=== Module Build Infrastructure === | === Module Build Infrastructure === | ||
| − | A device driver must be compiled against the kernel on which it will execute. Although it is possible to load and execute kernel modules built against a different kernel version, it is risky to do so unless you are certain that the module does not rely on any features of your new kernel. The easiest way to do this is to build the module within the kernel's own source tree. This ensures that as the developer changes the kernel configuration, | + | A device driver must be compiled against the kernel on which it will execute. Although it is possible to load and execute kernel modules built against a different kernel version, it is risky to do so unless you are certain that the module does not rely on any features of your new kernel. The easiest way to do this is to build the module within the kernel's own source tree. This ensures that as the developer changes the kernel configuration, this custom driver is automatically rebuilt with the correct kernel configuration. It is certainly possible to build your drivers outside the kernel source tree. However, in this case, you are responsible for making sure that your device driver build configuration stays in sync with the kernel you want to run your driver on. This typically includes compiler switches, the location of kernel header files, and kernel configuration options. |
For the sample driver introduced in Listing 8-1, the following changes were | For the sample driver introduced in Listing 8-1, the following changes were | ||
| Line 140: | Line 140: | ||
The patch shown in Listing 8-3 adds the single line (preceded by the +) to the | The patch shown in Listing 8-3 adds the single line (preceded by the +) to the | ||
| − | makefile found in ... /drivers/char. The additional lines of context are there | + | makefile found in <code>.../drivers/char</code>. The additional lines of context are there |
so that the patch utility can determine where to insert the new line. Our new | so that the patch utility can determine where to insert the new line. Our new | ||
| − | examples directory was added to the end of the list of directories already | + | ''examples'' directory was added to the end of the list of directories already |
being searched in this makefile, which seemed like a logical place to put it. | being searched in this makefile, which seemed like a logical place to put it. | ||
Other than for consistency and readability, the location is irrelevant. | Other than for consistency and readability, the location is irrelevant. | ||
| + | |||
Having completed the steps in this section, the infrastructure is now in place | Having completed the steps in this section, the infrastructure is now in place | ||
to build the sample device driver. The beauty of this approach is that the | to build the sample device driver. The beauty of this approach is that the | ||
| Line 152: | Line 153: | ||
Building for an arbitrary ARM system, the command line for building modules | Building for an arbitrary ARM system, the command line for building modules | ||
might look like this: | might look like this: | ||
| − | $ make ARCH= | + | |
| + | host$ '''make ARCH=arm CROSS COMPILE=xscale be- modules''' | ||
| + | |||
Listing 8-4 shows the build after a typical editing session on the module (all | Listing 8-4 shows the build after a typical editing session on the module (all | ||
other modules have already been built in this kernel source tree). | other modules have already been built in this kernel source tree). | ||
| + | |||
Listing 8-4. Module Build Output | Listing 8-4. Module Build Output | ||
| + | <pre> | ||
$ make ARCH=arm CROSS_COMPILE=xscale_be- modules | $ make ARCH=arm CROSS_COMPILE=xscale_be- modules | ||
| − | CHK include/linux/version.h | + | CHK include/linux/version.h |
make[l]: 'include/asm-arm/mach-types.h' is up to date. | make[l]: 'include/asm-arm/mach-types.h' is up to date. | ||
| − | CHK include/linux/utsrelease.h | + | CHK include/linux/utsrelease.h |
| − | SYMLINK include/asm -> include/asm-arm | + | SYMLINK include/asm -> include/asm-arm |
| − | CALL scripts/checksyscalls.sh | + | CALL scripts/checksyscalls.sh |
| − | CC [M] drivers/char/examples/hellol.o | + | CC [M] drivers/char/examples/hellol.o |
| − | Building modules, stage 2. | + | Building modules, stage 2. |
| − | MODPOST 76 modules | + | MODPOST 76 modules |
| − | LD [M] drivers/char/examples/hellol.ko | + | LD [M] drivers/char/examples/hellol.ko |
| − | + | </pre> | |
| − | + | ||
{{YoderFoot}} | {{YoderFoot}} | ||
Revision as of 00:04, 6 June 2012
Embedded Linux Class by Mark A. Yoder
Minimal Device Driver Example
Because Linux supports loadable device drivers, it is relatively easy to demonstrate a simple device driver skeleton. Listing 8-1 shows a loadable device driver module that contains the bare minimum structure to be loaded and unloaded by a running kernel.
Listing 8-1. Minimal Device Driver
/* Example Minimal Character Device Driver */
#include <linux/module.h>
static int
{
init hello_init(void)
printk(KERN_INFO "Hello Example Init\n");
return 0;
}
static void exit hello_exit(void)
{
printk("Hello Example Exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("Chris Hallinan");
MODULE_DESCRIPTION("Hello World Example");
MODULE_LICENSE("GPL");
The skeletal driver shown in Listing 8-1 contains enough structure for the kernel to load and unload the driver and to invoke the initialization and exit routines. Let's look at how this is done, because it illustrates some important high-level concepts that are useful for device driver development.
A device driver is a special kind of binary module. Unlike a stand-alone binary executable application, a device driver cannot simply be executed from a command prompt. The 2.6 kernel series requires that the binary be in a special "kernel object" format. When properly built, the device driver binary module contains a . ko suffix. The build steps and compiler options required to create the . ko module object can be complex. Here we outline a set of steps to harness the power of the Linux kernel build system without requiring you to become an expert in it, which is beyond the scope of this book.
Module Build Infrastructure
A device driver must be compiled against the kernel on which it will execute. Although it is possible to load and execute kernel modules built against a different kernel version, it is risky to do so unless you are certain that the module does not rely on any features of your new kernel. The easiest way to do this is to build the module within the kernel's own source tree. This ensures that as the developer changes the kernel configuration, this custom driver is automatically rebuilt with the correct kernel configuration. It is certainly possible to build your drivers outside the kernel source tree. However, in this case, you are responsible for making sure that your device driver build configuration stays in sync with the kernel you want to run your driver on. This typically includes compiler switches, the location of kernel header files, and kernel configuration options.
For the sample driver introduced in Listing 8-1, the following changes were made to the stock Linux kernel source tree to enable building this sample driver. We'll explain each step in detail:
- Starting from the top-level Linux source directory, create a directory under
.../drivers/charcalled examples. - Add a menu item to the kernel configuration to enable building examples and to specify a built-in or loadable kernel module.
- Add the new examples subdirectory to the
.../drivers/char/Makefileconditional on the menu item created in step 2. - Create a makefile for the new examples directory, and add the
hellol.cmodule object to be compiled conditional on the menu item created in step 2. - Create the driver
hello1.csource file from Listing 8-1.
Adding the examples directory under the .../drivers/char subdirectory is
self-explanatory. After this directory is created, two files are created in this
directory: the module source file itself from Listing 8-1, and the makefile for
the examples directory. The makefile for examples is quite trivial. It contains this single line:
obj-$(CONFIG_EXAMPLES) += hellol.o
Adding the menu item to the kernel configuration utility is a little more
involved. Listing 8-2 contains a patch that, when applied to the
.../drivers/char/Kconfig file from a recent Linux release, adds the
configuration menu item to enable our examples configuration option. In case
you're unfamiliar with the unified diff format, each line in Listing 8-2
preceded by a single plus character ( +) is inserted in the file between the indicated lines (those without the leading +).
Listing 8-2. Kconfig Patch for examples
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 6f31c94..0805290 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -4,6 +4,13 @@
menu "Character devices"
+config EXAMPLES
+ tristate "Enable Examples"
+ default M
+ ---help---
+ Enable compilation option for Embedded Linux Primer
+ driver examples
+
config VT
bool "Virtual terminal" if EMBEDDED
depends on !S390
When applied to Kconfig in the .../drivers/char subdirectory of a recent
Linux kernel, this patch results in a new kernel configuration option called CONFIG_EXAMPLES. As a reminder from our discussion on building the Linux
kernel in Chapter 4, "The Linux Kernel: A Different Perspective," the
configuration utility is invoked as follows (this example assumes the ARM architecture):
host$ make ARCH=arm CROSS_COMPILE=xscale_be- gconfig
After the configuration utility is invoked using a command similar to this one, our new Enable Examples configuration option appears under the Character devices menu, as indicated in the patch. Because it is defined as type tristate, the kernel developer has three choices:
(N) No. Do not compile examples. (Y) Yes. Compile examples and link with the final kernel image. (M) Module. Compile examples as a dynamically loadable module.
Figure 8-1 shows the resulting gconfig screen with the new configuration option added. A dash(-) in the check box selects module, as indicated in the M column on the right. A check mark in the check box selects yes, indicating that the driver module should be compiled as part of the kernel proper. An empty check box indicates that the option is not selected.
Figure 8-1. Kernel configuration with the examples module
Now that we have added the configuration option to enable compiling our
examples device driver module, we need to modify the makeflle In
.../drivers/char to instruct the build system to descend into our new examples subdirectory If the configuration option CONFIG_EXAMPLES is present in our configuration. Listlng 8-3 contains the patch for this against the
makefile in a recent Unux release.
Listing 8-3. Makefile Patch for examples
diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f957edf..f1b373d 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_MWAVE} += mwave/ obj-$(CONFIG_AGP) += agp/ obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ +obj-$(CONFIG_EXAMPLES) += examples/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/
The patch shown in Listing 8-3 adds the single line (preceded by the +) to the
makefile found in .../drivers/char. The additional lines of context are there
so that the patch utility can determine where to insert the new line. Our new
examples directory was added to the end of the list of directories already
being searched in this makefile, which seemed like a logical place to put it.
Other than for consistency and readability, the location is irrelevant.
Having completed the steps in this section, the infrastructure is now in place to build the sample device driver. The beauty of this approach is that the driver is built automatically whenever a kernel build is invoked. As long as the configuration option defined in Listing 8-3 is selected (either M or Y), the driver module is included in the build. Building for an arbitrary ARM system, the command line for building modules might look like this:
host$ make ARCH=arm CROSS COMPILE=xscale be- modules
Listing 8-4 shows the build after a typical editing session on the module (all other modules have already been built in this kernel source tree).
Listing 8-4. Module Build Output
$ make ARCH=arm CROSS_COMPILE=xscale_be- modules CHK include/linux/version.h make[l]: 'include/asm-arm/mach-types.h' is up to date. CHK include/linux/utsrelease.h SYMLINK include/asm -> include/asm-arm CALL scripts/checksyscalls.sh CC [M] drivers/char/examples/hellol.o Building modules, stage 2. MODPOST 76 modules LD [M] drivers/char/examples/hellol.ko
Embedded Linux Class by Mark A. Yoder