Migrating to Linux kernel 2.6 — Part 2: Migrating device drivers to Linux kernel 2.6
Feb 13, 2004 — by LinuxDevices Staff — from the LinuxDevices Archive — 4 viewsForeword — This whitepaper is the second in a series from TimeSys's “2.6 Linux Resource Center” on using the new Linux 2.6 kernel. Authored by TimeSys Senior Product Manager William von Hagen, the whitepapers in this series place special emphasis on the primary issues in migrating existing drivers, applications, and embedded Linux deployments to a Linux distribution based on the 2.6 kernel.
Material presented is largely vendor-neutral.
Enjoy! . . .
The kernel is the heart of the Linux operating system, managing all system threads, processes, resources, and resource allocation. Unlike most other operating systems, Linux enables users to reconfigure the kernel, which is usually done to reduce its size and add or deactivate support for specific devices or subsystems. Reconfiguring the kernel to remove support for unused devices and subsystems is quite common when developing embedded systems, because a smaller kernel requires less memory, increasing the resources available to your applications.
Device drivers are the software interface between your hardware and the Linux kernel. Device drivers are low-level, hardware-specific software components that enable devices to interact with more generic, higher-level application programming interfaces (APIs). Providing support for a specific subsystem or hardware interface, such as SCSI, USB, or PCMCIA, is very different than providing support for every SCSI, USB, or PCMCIA device. Testing every possible device that could be used over a specific subsystem is an impossibility; new devices are being made available every day. The kernel provides support for specific subsystems; device drivers provide support for specific devices that use those subsystems. Maintaining the separation of high-level APIs and low-level device functionality makes it relatively fast and easy to add support for new devices to existing systems by writing the appropriate driver for a new device and making it available to the kernel.
Linux device drivers can be integrated into the kernel in two different ways: either by compiling them into the kernel so that they are always available, or by compiling them into an object format that the kernel can load whenever access to a specific device is required. Kernel code that can be automatically loaded into the kernel is referred to as a loadable kernel module. When configuring the Linux kernel, each kernel configuration editor displays a description of available kernel configuration variables and enables you to specify whether each should be deactivated, compiled into the kernel, or compiled as a loadable kernel module.
Compiling device drivers into the kernel has the advantage that they are always instantly available, but each increases the size of the kernel that you are running. Compiling device drivers as loadable kernel modules implies some slight overhead when you search for and initially load the module in order to access the associated device, plus some small runtime overhead, but these are negligible compared to the savings in size and associated memory requirements. Writing device drivers as loadable kernel modules also provides significant advantages during development. As you develop and debug your device driver, you can dynamically unload the previous version and load the new version each time you want to test the new version. If your device driver is compiled into the kernel, you have to recompile the kernel and reboot each time that you want to test a set of iterative changes. Similarly, developing and deploying device drivers as loadable kernel modules simplifies maintaining them in the field, since the device driver can be updated as a separate system component without requiring a kernel update.
Configuring your kernel for support for loadable modules is done in the Loadable module support section of your kernel configuration editor. The Automatic kernel module loading option determines whether the kernel will automatically try to locate and load modules as they are needed by new devices or subsystems. The Module versioning support option (marked as experimental in the current 2.6 kernel source) adds extra versioning information to compiled modules at build-time. This information is designed to help increase module portability to kernels other than the one that they were compiled against. The Module unloading option is new to 2.6 kernel support for loadable kernel modules. You must enable this option if you want your kernel to be able to unload modules when they are no longer needed. This is especially important in resource-constrained and power-sensitive environments such as embedded systems. If you activate this option, you can also activate the Forced module unloading option, which enables you to forcibly unload modules regardless of whether the kernel believes that they are in use.
Once you have compiled and installed a loadable kernel module (and you are using a kernel with Automatic kernel module loading enabled), the loadable kernel module can be automatically loaded when the corresponding device is first accessed, provided that the depmod command has been used to create the module dependency tree. Loadable kernel modules are traditionally installed into a subdirectory of the system's /lib/modules directory – the name of this subdirectory is based on the values of the VERSION, PATCHLEVEL, SUBLEVEL, and EXTRAVERSION variables in the Makefile used to build your kernel.
The Linux 2.6 kernel introduces a new, unified framework for device drivers, which requires changes to custom device drivers that you may have developed to run under earlier versions of the Linux kernel. The new driver model provides a framework for full and complete support for device Plug and Play and power management by defining the interfaces that these subsystems can use when communicating with individual drivers. The new driver framework provides a much cleaner separation of responsibilities between buses and drivers. The 2.6 Linux kernel also introduces the sysfs filesystem to provide a hierarchical view of each system's device tree (and to prevent further overloading of the proc filesystem). The 2.6 Linux kernel also introduces a new naming convention for loadable kernel modules, using the .ko extension (kernel object) rather than the standard .o (object) extension used for loadable kernel modules in all previous stable releases of the Linux kernel.
Explaining all of the nuances of converting a device driver to the 2.6 model is outside the scope of this white paper, but is provided as a part of the documentation that accompanies the TimeSys Linux Development Suite (LDS). This white paper highlights the major structural differences between device drivers under the 2.6 kernel and earlier versions of the Linux kernel.
Updating the Basic Structure of a Device Driver
A standard template for a proper device driver under the Linux 2.4 kernel is the following:
#define MODULE
#include linux/module.h>
#include linux/config.h>
#include linux/init.h>
static int __init name_of_initialization_routine(void) {
/*
* code here
*/
}
static void __exit name_of_cleanup_routine(void) {
/*
* code here
*/
}
module_init(name_of_initialization_routine);
module_exit(name_of_cleanup_routine);
One common problem with many 2.4 and earlier device drivers is that they made assumptions about the names of the module initialization and cleanup functions. When writing device drivers for 2.4 and earlier Linux kernels, you did not have to register the names of the module initialization and cleanup/exit functions if you used the default names init_module() and cleanup_module() for these functions. This was always “wrong”, but it is no longer acceptable. Under the 2.6 kernel, you must use the module_init() and module_exit() macros to register the names of your initialization and exit routines. These declarations are only strictly necessary if you ever intend to compile the specified module into the kernel, but it's always best to prepare for that case.
Next, under the 2.6 kernel, the #define MODULE statement is no longer necessary, either in your code or in your Makefile. This symbol definition and others are now automatically defined (if necessary) and verified for you by the kernel build system, which you must now use when writing device drivers for the 2.6 kernel. Using the kernel build system to produce external modules is discussed later in this white paper.
These are the basic structural changes that you need to make to an existing module to get it to compile and load into a 2.6 kernel. However, once you load a module with this sort of structure, you will notice that an error message about a tainted module is displayed on standard output and in the system log (/var/log/messages). To eliminate this message, you need to add an instance of the MODULE_LICENSE() macro, such as the following:
MODULE_LICENSE("GPL");
This macro, introduced in later versions of the 2.4 kernel, defines the module as being licensed under the terms of GPL Version 2 or later. Other valid values are “GPL v2”, “GPL and additional rights”, “Dual BSD/GPL” (your choice of BSD or GPL licensing), “Dual MPL/GPL” (your choice of Mozilla or GPL licensing), and “Proprietary”, which is self-explanatory.
A generic template for a minimal 2.6 device driver would therefore be the following:
#include img src="/ldfiles/misc/lt.gif">linux/module.h>
#include img src="/ldfiles/misc/lt.gif">linux/config.h>
#include img src="/ldfiles/misc/lt.gif">linux/init.h>
MODULE_LICENSE("GPL");
static int __init name_of_initialization_routine(void) {
/* code goes here */
return 0;
}
static void __exit name_of_cleanup_routine(void) {
/* code goes here */
}
module_init(name_of_initialization_routine);
module_exit(name_of_cleanup_routine);
Aside from changes required to device drivers themselves, the most significant change to working with device drivers for the 2.6 Linux kernel are the changes in the build process described in the next section.
Changes to the Build Process for Modules
One basic change that affects everyone who works on a loadable device driver that is not part of the core kernel source code is the integration of external module compilation into the standard kernel build mechanism. If you are not using an integrated development environment such as TimeSys' TimeStorm that is capable of detecting kernel versions and automatically creating Makefiles that “do the right thing” for the 2.6 kernel, you will need to manually create Makefiles for your device drivers that are integrated into the kernel build process.
Under 2.4 and earlier Linux kernels, modules could be developed and compiled anywhere by passing appropriate compilation flags on the command line or in the module's Makefile. These flags consisted of two symbol definitions required when compiling modules, and a pointer to the directory containing the kernel include files, as in the following example that would produce a loadable kernel module named testmod.o:
gcc -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4.21/include -O2 -c testmod.c
When building modules for the 2.6 kernel, the process is simpler, but the requirements for successful compilation are different. By integrating the process of building external modules into the standard kernel build system, you don't have to manually specify module-oriented declarations such as MODULE, __KERNEL__, or newer symbols such as KBUILD_BASENAME and KBUILD_MODNAME. You also do not need to specify options such as -O2 (an optimization option for the gcc C compiler that enables optimizations such as inlining) – because your module is compiled like any other loadable kernel module, the make process automatically uses all mandatory flags. Module Makefiles are therefore much simpler, as in the following 2.6-compatible Makefile for the module testmod.ko:
obj-m := testmod.o
However, in order to build an external module, you must have write access to your kernel source tree in order to create temporary directories created and used during compilation. A sample command line for building a module for the 2.6 kernel is the following, which would be executed from within the directory containing your module's source code, as always:
# make -C /usr/src/linux-2.6.1 SUBDIRS=$PWD modules
This sample command assumes that your module source code and Makefile are located in the directory from which you are excuting the command. If you are not using a POSIX shell such as BASH, you can replace the SUBDIRS argument's use of the $PWD variable with SUBDIRS=`pwd`, which will use the actual pwd command to identify the working directory.
This sample command would produce output like the following:
make: Entering directory `/usr/src/linux-2.6.1'
*** Warning: Overriding SUBDIRS on the command line can cause
*** inconsistencies
make[1]: `arch/i386/kernel/asm-offsets.s' is up to date.
Building modules, stage 2.
MODPOST
CC /home/wvh/timesys/2.6/testmod/testmod.mod.o
LD [M] /home/wvh/timesys/2.6/testmod/testmod.ko
make: Leaving directory `/usr/src/linux-2.6.1'
The compilation step (the line containing CC) produces the basic object file for the module. The subsequent linker/loader step (the line containing LD) links in the file init/vermagic.o from the kernel source tree identified on the command line, which integrates information about the kernel against which the module was compiled and provides more stringent checks used when loading a module under 2.6. In general, the 2.6 module verification, loading, and unloading mechanisms are much more stringent than in previous versions of the Linux kernel.
Successful completion of the make command produces the module testmod.ko (“kernel object”), using the new kernel module naming convention. If you have modified your system's startup scripts to explicitly load modules by name, you will need to make sure that they now take the new naming convention into account when upgrading them to the 2.6 kernel, as discussed later in this series of white papers.
Highlights of Other 2.6-Related Changes
The 2.6 Linux kernel introduced many internal changes that require changes to existing drivers. Some of these include changes to the kernel's asynchronous I/O mechanism, DMA support layer, memory and page allocation mechanisms, the block device driver, a new generic disk interface, and many more. The functions used for allocating and managing memory and pages have changed, using a new standard interface known as mempool. Module reference counts, used to determine whether a module is in use and, if not, can safely be unloaded, are managed and manipulated differently. Task queues have been replaced with work queues. One significant change that affects a large number of different drivers is the new interface used for modules that take parameters. The MODULE_PARM() macro has been replaced by explicit parameter declarations made using the new module_param() macro.
The enhanced preemptibility and SMP-awareness of the 2.6 Linux kernel introduces some new concerns for driver writers. In uniprocessor, non-preemptible Linux kernels, some drivers may assume that there is no need to provide reentrancy between two processes since two processes could not be executing driver code at the same. Drivers should therefore use a spinlock or mutex to protect data that could be accessed from multiple processes. Considerations such as these are especially important when writing high-performance or real-time device drivers for embedded environments such as TimeSys Linux.
Other Considerations When Updating Drivers
Depending on the domain in which you are using the 2.6 kernel, other modifications to drivers may be necessary. For example, though the devfs filesystem has been present in the kernel since later versions of the 2.3 kernel and is marked as obsolete during 2.6 Linux kernel configuration, it is often used in domains such as embedded computing, where devfs provides flexibility and a reduced /dev footprint. The devfs filesystem is an intermediate step between the hardcoded device nodes used on earlier Linux and UNIX systems, and the integration of udev, hotplug, and the sysfs filesystem. Support for udev is currently being integrated into the standard 2.6 kernel, but is already available in commercial, reference 2.6 Linux distributions from TimeSys. If you are using another Linux distribution, you may find that devfs support and integration is attractive for the driver that you are working on.
If you want to use the devfs filesystem, you will need to explicitly activate support for it when building a kernel, in the File systems -> Pseudo filesystems section of your kernel configuration editor. Using devfs requires changes to how your device drivers identify any device nodes that they use. When using the traditional /dev directory as the location of Linux device descriptor files, a device driver registers a device by calling either the register_blkdev() or register_chrdev()functions, depending on whether the driver is registering a block or character device, and must know its major and minor device numbers “in advance”. This is the case even if you are using the new udev device mechanism, since udev is primarily a hotplug-aware set of scripts that automatically create and delete entries in the /dev directory.
When using the devfs device filesystem, device drivers must use the the devfs_register() system call to register their devices. Drivers can either continue to use pre-assigned major and minor numbers, or they can let devfs assign them automatically by specifying the DEVFS_FL_AUTO_DEVNUM flag to the devfs_register()call.
Conclusion
Changes to the kernel are always motivated by improvements, whether new features, simplification, or standardization. Each new Linux kernel introduces many changes that have implications for developers at all levels. This white paper provided an overview of 2.6-related changes to device drivers and the build process. Tools such as TimeSys TimeStorm that are already 2.6-aware provide updated driver templates and automatically create and manage Makefiles for 2.6 loadable kernel modules. However, if you are manually maintaining existing device drivers or developing new ones, you will need to incorporate 2.6-related changes into your manual procedures.
For general information about writing device drivers, “Linux Device Drivers, 2nd Edition” by Alessandro Rubini and Jonathan Corbet (ISBN 0596000081) is an excellent book on developing, debugging, and using Linux device drivers. The version of this book associated with the 2.4 kernel is available online at the URL http://www.xml.com/ldd/chapter/book/. A new version of this book is in progress to discuss 2.6 issues. Until a new edition is available, Mr. Corbet has written an excellent series of articles on writing device drivers for the 2.6 kernel. These are available at the Linux Weekly News. Similarly, the documentation provided with TimeSys' Linux Development Suite (LDS) provides detailed examples of writing device drivers, focusing on high-performance drivers for embedded use.
About the author: William von Hagen is a Senior Product Manager at TimeSys Corp., has been a Unix devotee for over twenty years, and has been a Linux fanatic since the early 1990s. He has worked as a system administrator, writer, developer, systems programmer, drummer, and product and content manager. Bill is the author of Linux Filesystems, Hacking the TiVo, SGML for Dummies, Installing Red Hat Linux 7, and is the coauthor of The Definitive Guide to GCC (with Kurt Wall) and The Mac OS X Power Users Guide (with Brian Profitt). Linux Filesystems is available in English, Spanish, Polish, and traditional Chinese. Bill has also written for publications including Linux Magazine, Mac Tech, Linux Format, and online sites such as Linux Planet and Linux Today. An avid computer collector specializing in workstations, he owns more than 200 computer systems.
Next?
The next segment of this white paper series provides an overview of the process of moving an existing desktop system to the 2.6 kernel. This white paper will highlight other software requirements imposed by the new kernel and administrative changes that you must make when migrating an existing system to the 2.6 kernel.
For additional whitepapers and other related information, visit TimeSys. TimeSys also offers Webinars on 2.6-related topics.
This article is part two of a series of whitepapers from TimeSys on Migrating to Linux kernel 2.6. The series includes:
- Customizing a 2.6-based Linux kernel
- Migrating device drivers to Linux kernel 2.6
- Using the 2.6 kernel with your current system
- Migrating custom Linux installations to 2.6
- Migrating applications to the 2.6 kernel and NPTL
This article was originally published on LinuxDevices.com and has been donated to the open source community by QuinStreet Inc. Please visit LinuxToday.com for up-to-date news and articles about Linux and open source.