Custom drivers
The exact format and integration of drivers (kernel modules) with the OS kernel differs for each OS and thus would be impossible to fully cover here. We will, however, look at how the driver for the RTC module we used earlier is implemented for Linux.
In addition, we will look at how to use an I2C peripheral from user space later in this chapter, in the club room monitoring example. Using a user space-based driver (library) is often a good alternative to implementing it as a kernel module.
The RTC functionality is integrated into the Linux kernel, with the code for it found in the /drivers/rtc folder (on GitHub, at https://github.com/torvalds/linux/tree/master/drivers/rtc).
The rtc-ds1307.c file contains two functions we need to read and set the RTC, respectively: ds1307_get_time() and ds1307_set_time(). The basic functionality of these functions is very similar to what we'll be using in the club room monitoring example later in this chapter, where we simply integrate I2C device support into our application.
A major advantage of communicating with I2C, SPI, and other such peripherals from user space is that we are not limited by the compile environment supported by the OS kernel. Taking the Linux kernel as an example, it is written mostly in C with some assembly. Its APIs are C-style APIs and thus we would have to use a distinctly C-style coding approach to writing our kernel modules.
Obviously, this would negate most of the advantages, not to mention the point, of attempting to write these modules in C++ to begin with. When moving our module code to user space and using it either as part of an application or as a shared library, we have no such limitations and can freely use any and all C++ concepts and functionality.
For completeness' sake, the basic template for a Linux kernel module looks as follows:
#include <linux/module.h> // Needed by all modules #include <linux/kernel.h> // Needed for KERN_INFO int init_module() { printk(KERN_INFO "Hello world.n"); return 0; } void cleanup_module() { printk(KERN_INFO "Goodbye world.n"); }
This is the requisite Hello World example, written in C++-style.
One final consideration when considering kernel- and user space-based driver modules is that of context switches. From an efficiency point of view, kernel modules are faster and have lower latency because the CPU does not have to switch from a user to kernel space context and back repeatedly to communicate with a device and pass messages from it back to the code communicating with it.
For high bandwidth devices (such as storage and capturing), this could make the difference between a smoothly functioning system and one that severely lags and struggles to perform its tasks.
However, when considering the club room monitoring example in this chapter and its occasional use of an I2C device, it should be obvious that a kernel module would be severe overkill without any tangible benefits.