Book Image

Linux Device Driver Development Cookbook

By : Rodolfo Giometti
Book Image

Linux Device Driver Development Cookbook

By: Rodolfo Giometti

Overview of this book

Linux is a unified kernel that is widely used to develop embedded systems. As Linux has turned out to be one of the most popular operating systems worldwide, the interest in developing proprietary device drivers has also increased. Device drivers play a critical role in how the system performs and ensure that the device works in the manner intended. By exploring several examples on the development of character devices, the technique of managing a device tree, and how to use other kernel internals, such as interrupts, kernel timers, and wait queue, you’ll be able to add proper management for custom peripherals to your embedded system. You’ll begin by installing the Linux kernel and then configuring it. Once you have installed the system, you will learn to use different kernel features and character drivers. You will also cover interrupts in-depth and understand how you can manage them. Later, you will explore the kernel internals required for developing applications. As you approach the concluding chapters, you will learn to implement advanced character drivers and also discover how to write important Linux device drivers. By the end of this book, you will be equipped with the skills you need to write a custom character driver and kernel code according to your requirements.
Table of Contents (14 chapters)
10
Additional Information: Managing Interrupts and Concurrency

To get the most out of this book

  • You should have a little knowledge of a non-graphical text editor such as vi , emacs, or nano. You can't connect an LCD display, a keyboard, and a mouse directly to the embedded kit to carry out little modifications to text files, so you should have a working knowledge of these tools to do such modifications remotely.
  • You should know how to manage an Ubuntu system, or at least a generic GNU/Linux-based one. My host PC is running on Ubuntu 18.04.1 LTS, but you can use also a newer Ubuntu LTS release, or a Debian-based system with a few modifications. You can also use another GNU/Linux distribution, but this will require a little effort from you, mainly with regard to the installation of cross-compile tools, libraries dependencies, and package management.
    Foreign systems, such as Windows, macOS, and others, are not covered by this book due the fact that you should not use low-tech systems to develop code for a high-tech system!
  • Working knowledge of the C programming language, how a C compiler works, and how to manage a makefile are all mandatory requirements.

Download the example code files

You can download the example code files for this book from your account at www.packt.com. If you purchased this book elsewhere, you can visit www.packt.com/support and register to have the files emailed directly to you.

You can download the code files by following these steps:

  1. Log in or register at www.packt.com.
  2. Select the SUPPORT tab.
  3. Click on Code Downloads & Errata.
  4. Enter the name of the book in the Search box and follow the onscreen instructions.

Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:

  • WinRAR/7-Zip for Windows
  • Zipeg/iZip/UnRarX for Mac
  • 7-Zip/PeaZip for Linux

The code bundle for the book is hosted on GitHub at https://github.com/giometti/linux_device_driver_development_cookbook. In case there's an update to the code, it will be updated on the existing GitHub repository.

The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Linux-Device-Driver-Development-Cookbook. In case there's an update to the code, it will be updated on the existing GitHub repository.

We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!

Download the color images

Conventions used

There are a number of text conventions used throughout this book.

Code words in text folder names, filenames, file extensions, pathnames, dummy URLs and user input are shown as follows: "To get the preceding kernel messages, we can use both the dmesg and tail -f /var/log/kern.log commands."

A block of code is set as follows:

#include <stdio.h>

int main(int argc, char *argv[])
{
printf("Hello World!\n");

return 0;
}

You should note that most code in this book has 4-space indentation, while the example code you can find in the files provided with this book on the GitHub or Packt sites uses 8-space indentation. So, the preceding code will look as follows:

#include <stdio.h>

int main(int argc, char *argv[])
{
printf("Hello World!\n");

return 0;
}

Obviously, they are perfectly equivalent in practice!

Any command-line input or output on the embedded kit used in this book is presented as follows:

# make CFLAGS="-Wall -O2" helloworld
cc -Wall -O2 helloworld.c -o helloworld

Commands are in bold, while their output is in normal text. You should also notice that the prompt string has been removed due to space constraints; in fact, on your Terminal, the complete prompt should look like the following:

root@espressobin:~# make CFLAGS="-Wall -O2" helloworld
cc -Wall -O2 helloworld.c -o helloworld

Note also that due to space constraints in the book, you may encounter very long command lines as follows:

$ make CFLAGS="-Wall -O2" \
CC=aarch64-linux-gnu-gcc \
chrdev_test
aarch64-linux-gnu-gcc -Wall -O2 chrdev_test.c -o chrdev_test

Otherwise, I have had to break the command line. However, in some special cases, you can find broken output lines (especially on kernel messages) as follows:

[ 526.318674] mem_alloc:mem_alloc_init: kmalloc(..., GFP_KERNEL) =ffff80007982f
000
[ 526.325210] mem_alloc:mem_alloc_init: kmalloc(..., GFP_ATOMIC) =ffff80007982f
000

Unluckily, these lines cannot easily be reproduced in a printed book, but you should consider them as a single line.

Any command-line input or output given on my host computer as a non-privileged user is written as follows:

$ tail -f /var/log/kern.log

When I need to give a command as a privileged user (root) on my host computer, the command-line input or output is then written as follows:

# insmod mem_alloc.ko

You should note that all privileged commands can be executed by a normal user, too, by using the sudo command in the following format:

$ sudo <command>

So, the preceding command can be executed by a normal user as follows:

$ sudo /insmod mem_alloc.ko

Kernel and logging messages

On several GNU/Linux distribution, kernel messages have this usual form:

[ 3.421397] mvneta d0030000.ethernet eth0: Using random mac address 3e:a1:6b:
f5:c3:2f

This is a quite a long line for this book, so that's why we drop the characters from the start of each line up to the point where the real information begins. So, in the preceding example, the lines output will be reported as follow:

mvneta d0030000.ethernet eth0: Using random mac address 3e:a1:6b:f5:c3:2f

However, as already said, if the line is still too long, it will be broken anyway.

Long outputs, or repeated or less important lines in the Terminal, are dropped by replacing them with three dots, ..., as follows:

output begin
output line 1
output line 2
...
output line 10
output end

When the three dots are at the end of a line, it means that the output continues, but I decided cut it for space reasons.

File modifications

When you should modify a text file, I'm going to use the unified context diff format since this is a very efficient and compact way to represent a text modification. This format can be obtained by using the diff command with the -u option argument, or by using the git diff command within a git repository.

As a simple example, let's consider the following text in file1.old :

This is first line
This is the second line
This is the third line
...
...
This is the last line

Suppose we have to modify the third line, as highlighted in the following snippet:

This is first line
This is the second line
This is the new third line modified by me
...
...
This is the last line

You can easily understand that reporting the whole file each time for a simple modification is unnecessary and space-consuming; however, by using the unified context diff format, the preceding modification can be written as follows:

$ diff -u file1.old file1.new
--- file1.old 2019-05-18 14:49:04.354377460 +0100
+++ file1.new 2019-05-18 14:51:57.450373836 +0100
@@ -1,6 +1,6 @@
This is first line
This is the second line
-This is the third line
+This is the new third line modified by me
...
...
This is the last line

Now, the modification is very clear and written in a compact form! It starts with a two-line header, where the original file is preceded by --- and the new file is preceded by +++. Then, it follows one or more change hunks that contain the line differences in the file. The preceding example has just one hunk where the unchanged lines are preceded by a space character, while the lines to be added are preceded by a + character and the lines to be removed are preceded by a - character.

Nonetheless, for space reasons, most patches reproduced in this book have reduced indentation in order to fit the width of printed pages; however, they are still perfectly readable. For the full patch, you should refer to the provided files on GitHub or the Packt site.

Serial and network connections

In this book, I'm mainly going to use two different kinds of connections to interact with the embedded kit: the serial console, and an SSH terminal and Ethernet connection.

The serial console, implemented over a USB connection, is mainly used to manage the system from the command line. It's largely used for monitoring the system, and especially for taking control of kernel messages.

An SSH terminal is quite similar to the serial console, even if is not exactly the same (for example, kernel messages do not automatically appear on a Terminal), but it can be used in the same manner as a serial console to give commands and edit files from the command line.

In the chapters, I'm going to use a Terminal on the serial console or over an SSH connection to give the most of the commands and configuration settings needed to implement all the prototypes explained in this book.

To get access to the serial console from your host PC, you can use the minicon command, as follows:

$ minicom -o -D /dev/ttyUSB0

However, in Chapter 1 , Installing the Development System, these aspects are explained and you should not worry about them. Note also that on some systems, you may need root privileges to get access to the /dev/ttyUSB0 device. In this case, you can fix this issue or by using the sudo command or, better, by properly adding your system's user to the right group by using the following command:

$ sudo adduser $LOGNAME dialout

Then log out and log back in again, and you should be able to access the serial devices without any problem.

To get access to the SSH Terminal, you can use Ethernet connection. It is used mainly to download files from the host PC or the internet and can be established by connecting an Ethernet cable to the embedded kit's Ethernet port, and then configuring the port accordingly to the reader's LAN settings (see all the instructions in Chapter 1 , Installing the Development System).

Other conventions

Bold: Indicates a new term, an important word, or words that you see onscreen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: "Select System info from the Administration panel."

Warnings or important notes appear like this.
Tips and tricks appear like this.