Dynamic Kernels: Discovery
The last issues Kernel Korner introduced problems related to loading and unloading a custom module, but didn't uncover the code to actually perform these tasks. This time, we are going to look at some finer details of module-writing, in order to begin showing the actual code for our character device driver.
Although a smart driver should be able to autodetect the hardware it looks for, autodetection is not always the most sensible implementation, because it may be tricky to design. It is wise to provide a way to specify as many details as possible at load time, in order to test your driver before scratching your head and deciding to implement autodetection. Moreover, autodetection may fail if “similar” hardware is installed in the computer. A minor project can simply avoid autodetection altogether.
To configure the driver at load time, we'll exploit insmod's capability to assign integer values to arbitrary variables in the driver. We'll thus declare a (public) integer variable for each configurable item, and we'll make sure that the default value will trigger autodetection.
Configuring multiple boards at load time is left as an exercise to the reader (after reading the manpage for insmod); this implementation allows specification of a single board: for the sake of simplicity additional boards are only reachable through autodetection.
The kernel is a complex application, and it is vital to keep its namespace as tidy as possible. This means both to use private symbols wherever it is possible, and to use a common prefix for all the symbols you define.
A production environment will only declare init_module() and cleanup_module(), which are used to load and unload the driver, and any load-time configuration variables. Nothing else needs to be public, because the module is accessed through pointers, not by name.
However, when you are developing and testing your code, you'll need your functions and data structures in the public symbol table in order to access them with your favorite debugging tool.
The easiest way to accomplish this dual need is to always use your own prefix in names, declare all of your symbols Static (note the capital `S'), and include the following five lines at the top of your driver:
#ifdef DEBUG_modulename # define Static /* nothing */ #else # define Static static #endif
Real static symbols (such as persistent local variables) may thus be declared static, while debuggable symbols are declared Static
In this page, the whole code for the initialization function is uncovered. This is skeletal code, as the skel name suggests: a real-world device usually has slightly more than two I/O ports.
The most important thing to remember here is to release all the resources you already asked for whenever you find an error condition. This kind of task is well handled by the (otherwise unloved) goto statement: code duplication is avoided by jumping to the resource-release part of the function in case of error.
The fragment of code shown accepts load-time configuration for the major number, for the base address of the board's I/O ports, and for the IRQ number. For each “possible” board (in the I/O space), the autodetection function is called. If no boards are detected, init_module() returns -ENODEV to tell insmod that no devices are there.
Sometimes it is wise to allow the driver to be loaded even if its hardware is not installed in the computer. I implement such code in order to develop most of my driver at home. The trick is to have a configuration variable (skel_fake) which allows you to fake a nonexistent board. You can look at the implementation in my own drivers. “Faking boards” is a powerful way to start writing code before you get the hardware, or to test support for two boards even if you only own one of them.
The role of cleanup_module() is to shut down the device and release any resources allocated by init_module(). Our sample code cycles through the array of boards and releases I/O ports and the IRQ, if any. Finally, the major number is released. The initial check for MOD_IN_USE is redundant if you're running a recent kernel, but a wise thing to put in production code, because your customers or users may be running old Linux kernels.
The sample code for init_module() and cleanup_module() is shown in Listing 1. The prefix skel_ is used for all non-local names. The code here is quite simplified, in that it lacks some error-checking, which is vital in production-quality source code.
Fast/Flexible Linux OS Recovery
On Demand Now
In this live one-hour webinar, learn how to enhance your existing backup strategies for complete disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible full-system recovery solution for UNIX and Linux systems.
Join Linux Journal's Shawn Powers and David Huffman, President/CEO, Storix, Inc.
Free to Linux Journal readers.Register Now!
- The Qt Company's Qt Start-Up
- Devuan Beta Release
- May 2016 Issue of Linux Journal
- Open-Source Project Secretly Funded by CIA
- EnterpriseDB's EDB Postgres Advanced Server and EDB Postgres Enterprise Manager
- The US Government and Open-Source Software
- The Death of RoboVM
- BitTorrent Inc.'s Sync
- The Humble Hacker?
- New Container Image Standard Promises More Portable Apps
In modern computer systems, privacy and security are mandatory. However, connections from the outside over public networks automatically imply risks. One easily available solution to avoid eavesdroppers’ attempts is SSH. But, its wide adoption during the past 21 years has made it a target for attackers, so hardening your system properly is a must.
Additionally, in highly regulated markets, you must comply with specific operational requirements, proving that you conform to standards and even that you have included new mandatory authentication methods, such as two-factor authentication. In this ebook, I discuss SSH and how to configure and manage it to guarantee that your network is safe, your data is secure and that you comply with relevant regulations.Get the Guide