Figure 8-1 shows the resulting gconfig screen with the new configuration option added. The dash (-) in the check box selects (M)odule, as indicated in the M column on the right. A check mark in the check box selects (Y)es, 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 Examples module

Now that we have added the configuration option to enable compiling our examples device driver module, we need to modify the makefile 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. Listing 8-3 contains the patch for this against the makefile in a recent Linux release.
Listing 8-3. Makefile Patch for Examples
diff -u ~/base/linux-2.6.14/drivers/char/Makefile ./drivers/char/Makefile
--- ~/base/linux-2.6.14/drivers/char/Makefile
+++ ./drivers/char/Makefile
@@ -88,6 +88,7 @@
obj-$(CONFIG_DRM) += drm/
obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_IPMI_HANDLER) += ipmi/
+obj-$(CONFIG_EXAMPLES) += examples/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck- timer.o
The patch in Listing 8-3 adds the single line (preceded by the + character) 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 example 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:
$ make ARCH=arm CROSS_COMPILE=xscale_be- modules
Listing 8-4 shows the build output 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[1]: 'arch/arm/kernel/asm-offsets.s' is up to date.
make[1]: 'include/asm-arm/mach-types.h' is up to date.
CC [M] drivers/char/examples/hello1.o
Building modules, stage 2.
MODPOST
LD [M] drivers/char/examples/hello1.ko
8.1.5. Installing Your Device Driver
Now that this driver is built, we can load and unload it on a running kernel to observe its behavior. Before we can load the module, we need to copy it to an appropriate location on our target system. Although we could put it anywhere we want, a convention is in place for kernel modules and where they are populated on a running Linux system. As with module compilation, it is easiest to let the kernel build system do that for us. The makefile target modules_install automatically places modules in the system in a logical layout. You simply need to supply the desired location as a prefix to the default path.
In a standard Linux workstation installation, you might already know that the device driver modules live in /lib/modules/<kernel-version>/... ordered in a manner similar to the device driver directory hierarchy in the Linux kernel tree.[62] The <kernel-version> string is produced by executing the command uname -r on your target Linux system. If you do not provide an installation prefix to the kernel build system, by default, your modules are installed in your own workstation's /lib/modules/... directory. This is probably not what you had intended. You can point to a temporary location in your home directory and manually copy the modules to your target's file system. Alternatively, if your target embedded system uses NFS root mount to a directory on your local development workstation, you can install the modules directly to the target file system. The following example assumes the latter.
$ make ARCH=arm CROSS_COMPILE=xscale_be-
INSTALL_MOD_PATH=/home/chris/sandbox/coyote-target
modules_install
This places all your modules in the directory coyote-target, which on this example system is exported via NFS and mounted as root on the target system.[63]
8.1.6. Loading Your Module
Having completed all the steps necessary, we are now in a position to load and test the device driver module. Listing 8-5 shows the output resulting from loading and subsequently unloading the device driver on the embedded system.
Listing 8-5. Loading and Unloading a Module
$ modprobe hello1 <<< Load the driver
Hello Example Init
$ modprobe -r hello1 <<< Unload the driver
Hello Example Exit
$
You should be able to correlate the output with our device driver source code found in Listing 8-1. The module does no work other than printing messages to the kernel log system via printk(), which we see on our console.[64] When the module is loaded, the module-initialization function is called. We specify the initialization function that will be executed on module insertion using the module_init() macro. We declared it as follows:
module_init(hello_init);
In our initialization function, we simply print the obligatory hello message and return. In a real device driver, this is where you would perform any initial resource allocation and hardware device initialization. In a similar fashion, when we unload the module (using the modprobe -r command), our module exit routine is called. As shown in Listing 8-1, the exit routine is specified using the module_exit() macro.
That's all there is to a skeletal device driver capable of live insertion in an actual kernel. In the sections to