Device Driver is software that is used to control a hardware device and make it work. It acts as an interface between the user application and the hardware.
A device driver provides the software/hardware interface needed to control external hardware. For example, the ethernet card on your computer communicates to your CPU through the PCIe bus (on which other peripherals are attached, each with their own device driver). The ethernet driver in your OS knows how to talk to that particular ethernet card through the bus. The driver handles network traffic by reading from and writing to hardware registers on the ethernet card, providing a software “wrapper” around the hardware, abstracting the low-level details away from the OS and its user applications.
Any user-application reaches out to the device driver for any interaction with the hardware device and the device driver checks for various conditions (like the permissions the user-application has or not etc.). After all the relevant conditions are met, the corresponding device driver responds and completes the task requested by the user application (such as read/write to the device etc.). Finally, when the driver has completed its interaction with the hardware device, it responds back to the user-application with the information requested.
As for learning to write device drivers, where you should begin depends on how comfortable you are with hardware and programming. If you’re a complete novice in both, buy an Arduino and a breadboard, and learn to make an LED blink with a push button using the beginner-friendly libraries. As you gain confidence, experiment with sensors that have a serial (UART) interface. Try to build complete systems that do useful things; you’ll get the hang of it soon enough.
So, the device driver is like a black box that means, if any user-application wants to interact with the hardware, it must go through the corresponding device driver only and not directly as it might cause any damage to the hardware.
Device Driver as an interface. Copyright © VLSIFacts
So the very next question that would come into our mind is-“how does the driver communicate with the device?”
The communication is through the computer bus communication subsystem (say I2C protocol) to which the hardware is connected.
Two more points worth mentioning is that –
1. The drivers are hardware dependent, for eg. the driver for an accelerometer sensor would be drastically different from the drivers for the camera as both the devices are used for different purposes.
2. The drivers are Operating System specific (OS), for eg. The same camera driver for Linux OS would be considerably different from that for Windows OS as different OS exposes different APIs for middlewares.
Device drivers help high-level application programmers to not worry about the specifications of the hardware, they just need to know the API that can be used to communicate to the driver.
For eg., a high-level application for interacting with a serial port may simply have two functions for sending data-func1 and for receiving data func2. At a lower level, a device driver implementing these functions would communicate to the particular serial port controller installed on a user’s computer.
Some drivers get loaded automatically when the system starts and some drivers load when we actually insert the device into the system. Almost all of us have seen the “installing the device software” notification in windows when we insert any new device into our system during runtime.
Linux allows the drivers to be loaded/unloaded on runtime also, that’s why the drivers are also known as loadable modules or just modules. Due to this very useful feature, Linux OS is used in servers also so that required modules can be loaded and unloaded at runtime and we don’t need to restart the system every time.
Drivers can run in both user mode as well as in kernel mode. The main advantage of running in user mode is that if the driver crashes it won’t crash the kernel. On the other hand, user/kernel-mode transitions usually impose a considerable performance overhead, thereby prohibiting user-mode drivers for low latency and high throughput requirements.
There are broadly three kinds of device drivers in Linux-
- Character Device Drivers– eg. Keyboard drivers, camera drivers, and sensor drivers- these drivers work on devices which transmit data per byte.
- Block Device Drivers– eg. USB, Hard Drive drivers- these drivers work on devices that transmit the block of data.
- Network Device Drivers– These are the hardware used by networking hardware, these are similar to block device drivers in a sense that they work on packets but they have some differences too which we will see later.
In Operating System, the device drivers play a vital role. On the topmost level, the user application runs. Below that, the System Call Interface (SCI) is there which communicates with the device driver that ultimately interacts with the hardware. The following picture depicts the above statements.
The SCI interacts with the Kernel space (kernel subsystems, features implemented and software support in the below diagram). As we have already discussed the kernel subsystems, we would not be going into details of them. The architecture-dependent code forms the CPU, and similarly, the various examples of device drivers can be seen at the bottom-most layer of the picture.
Role of Device Driver in OS (Source: Quora)
Linux Architecture
Before we go into device drivers, let us get a brief of the Linux architecture. The Linux virtual memory is divided into two partitions namely user space and kernel space to protect data and functionality from fault or any unwanted access. The following picture gives a basic understanding of Linux architecture.
The architecture of Linux OS (Source: www.ibm.com)
e Linux architecture. The Linux virtual memory is divided int
Userspace is the memory space where user applications run whereas Kernel space is the privileged space where the operating system (kernel) executes and provides its services.
The GNU C library (glibc) provides APIs (Application Programming Interfaces) that interacts with the kernel through system calls (syscalls). For example, when we call printf () in our C program, it internally calls the write() syscall. So, the System Call Interface (SCI) acts as a medium between the user space and kernel space. The kernel is the core of the operating system that loads first and remains in the main memory. It is responsible for memory management, process management, virtual file system, and network stack. The following picture illustrates the Linux Kernel Subsystems.
Linux Kernel Subsystems (Source: www.ibm.com)
Process management is focused on the management and execution of processes (called threads in the kernel). Threads represent individual virtualization of the processor (i.e. each thread has its own thread code, data, stack, and CPU registers). However, in user space, the term process is typically used. The kernel provides APIs through the SCI to create a new process (fork, exec, or Portable Operating System Interface [POSIX] functions), stop a process (kill, exit), and communicate and synchronize between them (signal, or POSIX mechanisms).
Memory manager allows multiple processes to securely share the main memory. Also, it supports virtual memory that allows Linux to run processes that require more memory than available with the system.
Network Stack provides the capabilities to access several networking standards and networking hardware.
VFS (Virtual File System) provides a common file system for all devices to abstract the details of various hardware devices and supports many file systems that are compatible with other operating systems as well. Found in <linux/fs.h>
General implementation steps
- Understand the device characteristic and supported commands.
- Map device specific operations to unix file operation
- Select the device name (user interface)
- Namespace (2-3 characters, /dev/lp0)
- (optional) select a major number and minor (a device special file creation) for VFS interface
- Mapping the number to right device sub-routines
- Implement file interface subroutines
- Compile the device driver
- Install the device driver module with a loadable kernel module (LKM)
- or Rebuild (compile) the kernel
Since the basics of device drivers is clear so, let’s begin by writing a simple loadable module. As we write we will understand the stuffs required to write a device driver.
It is said that to begin any programming chapter we must write a hello world program otherwise something bad might happen to us, so let’s start with it.
Hello world program-
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE(“GPL”);
static int hello_init(void)
{
printk(KERN_ALERT “Hello world”);
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT “Goodbye”);
}
module_init(hello_init);
module_exit(hello_exit);
————————————————————————————————–
Lets write this program in vim or nano editor and save as hello.c. Now, let’s see how can we compile and run this program.
To compile a kernel module we need to create a Makefile for the same, and we keep the make file in the same directory as the file else we will have to give the full path of Makefile.
Makefile for the code shown above would be
obj-m +=hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
obj-m shows that we want to build this program as a module.
Makefiles are a unique species in its own; in a sense that we have to follow some strict guidelines otherwise, it won’t work.
The two very important considerations that we need to take care of are-
1. While saving the Makefile its name should have capital M and not small m.
2. Make word after all: and clean: should be shifted by one tab from the left end. The module is compiled and built by giving make command in the same directory (test directory here) as the hello.c and Makefile we can generate hello.ko file. (ko means kernel object).
We can see that hello.ko file got generated.
Let’s try to understand the structure of this program-
1. Like all other programs that we have written, here we need to include some headers. These header files live in /include/Linux folder.init.h header file is used to start the init process while module.h file is used to add module capability to this program.
2. The next line is used to specify the license that we are using for this program- its GPL (GNU Public License).
3. The given program is written in userspace. To insert this program into kernel space as a module we give insmod command.
4. The command would look like insmod hello.ko (hello.ko is the kernel object file generated when we compile this program as a module). After inserting the module in Kernel space we can see if it is present or not by giving lsmod command.
5. Similarly rmmod hello.ko command is used to remove the module from kernel space.
6. When we give insmod hello.ko command the initialization function hello_init gets called and module gets loaded into kernel space and when we give rmmod hello.ko command the exit function hello_exit gets called and the module is removed from the kernel space.
7. printk is the kernel space equivalent of userspace function printf.
8. KERN_ALERT is a type of kernel log levels (higher the log level more logs will appear while the program runs). We will see other log levels in an advanced topic.
You must be wondering where did those logs go that was supposed to get printed when we insert and remove modules. As I told you those are kernel logs that’s why they get stored in Kernel log buffer which you can see by giving dmesg command.
Virtual Device Drivers
Virtual device drivers are used where there is no physical hardware present to perform that operation but we need to give the OS a feel that the hardware is there. The best example would be IPC driver (Inter-Processor Communication). When we run an OS in a virtualized environment then also we use virtual device drivers. For example, a virtual network adapter is used with a virtual private network, while a virtual disk device is used with iSCSI(Internet Small Computer Systems Interface). One better example of virtual device drivers can be Daemon Tools.
8 logs level for print function
KERN_EMERG: This log level gets used only for an emergency purpose.Ex: process stops.
KERN_ALERT: To attract the attention.
KERN_ERR: To display the error.
KERN_CRIT: To told about Critical serious hardware or software problem.
KERN_WARNING: warning message of danger.
KERN_NOTICE: Simple notice message.
KERN_INFO: Provide Information
KERN_DEBUG: To see log messages during debugging.
It is advisable for you to perform all these steps on your Linux system.
Summing up
So, to conclude, a device driver is a piece of software that drives a device, though there are so many classifications. In case it drives only another piece of software, we call it just a driver. Examples are file-system drivers, usbcore, etc. Hence, all device drivers are drivers, but all drivers are not device drivers.
I will address remaining minutiae in our future posts.
Until then, stay tuned and muchas gracias.
About IPCS Automation
IPCS- Ingenious Power and Control Systems P LTD is an ISO certified Automation training institute in India and also outside Part of India now they are providing PLC Course in Chennai.