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

Doing native compiling on foreign hardware

Before ending this chapter, I'd like to introduce an interesting cross-platform system that's useful when we need several different OSes running on your development PC. This step is very useful when we need a complete OS to compile a device driver or an application but we do not have a target device to compile onto. We can use our host PC to compile code for a foreign hardware across different OS and OS release.

Getting ready

During my career, I worked with tons of different platforms and having one virtual machine for all of them is very complex and really system consuming (especially if we decide to run several of them at the same time!). That's why it can be interesting to have a lightweight system that can execute foreign code on your PC. Of course, this method cannot be used to test a device driver (we need real hardware for that), but we can use it to run a native compiler and/or native userspace code really quickly just in case our embedded platform is not working. Let's see what I'm talking about.

In the Setting up the target machine recipe, regarding the Debian OS installation, we used the chroot command to set up the root's password. This command worked thanks to QEMU; in fact, in the debian-stretch-arm64 directory, we have an ARM64 root filesystem, which can be executed on an x86_64 platform by using QEMU only. It's then clear that, in this manner, we should be able to execute whatever command we'd like and, of course, we will be able to execute the Bash shell as in the next recipe.

How to do it...

Now it's time to see how chroot works:

  1. Execute an ARM64 bash command by using our x86_64 host, as follows:
$ sudo chroot debian-stretch-arm64/ bash
bash: warning: setlocale: LC_ALL: cannot change locale (en_GB.UTF-8)
root@giometti-VirtualBox:/#
  1. Then, we can use each ARM64 command as we did on the ESPRESSObin; for example, to list files into the current directory; we can use the following:
# ls /
bin dev home media opt root sbin sys usr
boot etc lib mnt proc run srv tmp var
# cat /etc/hostname
espressobin

However, there are some traps; for instance, we completely miss the /proc and /sys directories and programs, which rely on them and will fail for sure:

# ls /{proc,sys}
/proc:

/sys:
# ps
Error: /proc must be mounted
To mount /proc at boot you need an /etc/fstab line like:
proc /proc proc defaults
In the meantime, run "mount proc /proc -t proc"

To resolve these problems, we can manually mount these missing directories before executing chroot, but this is quite annoying due to the fact that they are so many, so we can try using the schroot utility, which, in turn, can do all of these steps for us. Let's see how.

For detailed information regarding schroot, you can see its man pages with man schroot.

Installing and configuring schroot

This task is quite trivial in Ubuntu:

  1. First of all, we install the program in the usual way:
$ sudo apt install schroot
  1. Then, we have to configure it in order to correctly enter into our ARM64 system. To do so, let's copy the root filesystem created before into a dedicated directory (where we can also add any other distributions we wish to emulate with schroot):
$ sudo mkdir /srv/chroot/
$ sudo cp -a debian-stretch-arm64/ /srv/chroot/
  1. Then, we must create a proper configuration for our new system by adding a new file into the schroot configuration directory, as follows:
$ sudo bash -c 'cat > /etc/schroot/chroot.d/debian-stretch-arm64 <<__EOF__
[debian-stretch-arm64]
description=Debian Stretch (arm64)
directory=/srv/chroot/debian-stretch-arm64
users=giometti
#groups=sbuild
#root-groups=root
#aliases=unstable,default
type=directory
profile=desktop
personality=linux
preserve-environment=true
__EOF__'
Note that the directory parameter is set to the path holding our ARM64 system and users is set to giometti, which is my username (this is a comma-separated list of users that are allowed access to the chroot environment—see man schroot.conf).

Looking at the preceding settings, we see that the profile parameter is set to desktop; this means that it will be taking into account all files in the /etc/schroot/desktop/ directory. In particular, the fstab file holds all mount points we'd like to be mounted into our system. So, we should verify that it holds at least the following lines:

# <filesystem> <mount point> <type> <options> <dump> <pass>
/proc /proc none rw,bind 0 0
/sys /sys none rw,bind 0 0
/dev /dev none rw,bind 0 0
/dev/pts /dev/pts none rw,bind 0 0
/home /home none rw,bind 0 0
/tmp /tmp none rw,bind 0 0
/opt /opt none rw,bind 0 0
/srv /srv none rw,bind 0 0
tmpfs /dev/shm tmpfs defaults 0 0
  1. Now, we have to restart the schroot service, as follows:
$ sudo systemctl restart schroot
Note that you can also restart using the old-fashioned way:
$ sudo /etc/init.d/schroot restart
  1. Now we can list all available environments by asking them to schroot, as follows:
$ schroot -l
chroot:debian-stretch-arm64
  1. OK, everything is in place and we can enter into the emulated ARM64 system:
$ schroot -c debian-stretch-arm64
bash: warning: setlocale: LC_ALL: cannot change locale (en_GB.UTF-8)
Since we haven't installed any locale support, the preceding warning is quite obvious and it should be safely ignored.
  1. Now, to verify we're really executing ARM64 code, let's try some commands. For example, we can ask for some system information with the uname command:
$ uname -a
Linux giometti-VirtualBox 4.15.0-43-generic #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018 aarch64 GNU/Linux

As we can see, the system says that its platform is aarch64, which is ARM64. Then, we can try to execute our helloworld program that was cross-compiled before; since, after chroot, the current directory is not changed (and our home directory is still the same), we can simply go back where we did the compilation and then execute the program as usual:

$ cd ~/Projects/ldddc/github/chapter_1/
$ file helloworld
helloworld: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=c0d6e9ab89057e8f9101f51ad517a253e5fc4f10, not stripped
$ ./helloworld
Hello World!

The program still executes as when we were on an ARM64 system. Great!

Configuring the emulated OS

What we just saw about schroot is nothing if we do not configure our new system to do native compilation, and to do so, we can use every Debian tool we use on our host PC:

  1. To install a complete compiling environment, we can issue the following command once inside the schroot environment:
$ sudo apt install gcc make \
bison flex ncurses-dev libssl-dev
Note that sudo will ask your usual password, that is, the password you currently use to log in to your host PC.
You might not get a password request from sudo with the following error message:
sudo: no tty present and no askpass program specified
You can try executing the preceding sudo command again, adding to it the -S option argument.
It could be possible that the apt command will notify you that some packages cannot be authenticated. Just ignore this warning and continue installation, answering yes by pressing the Y key.

If everything works well, we should now be able to execute every compiling command we used before. For instance, we can try to recompile the helloworld program again but natively (we should remove the current executable in order; make will try to recompile it again):

$ rm helloworld
$ make CFLAGS="-Wall -O2" helloworld
cc -Wall -O2 helloworld.c -o helloworld
$ file helloworld
helloworld: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=1393450a08fb9eea22babfb9296ce848bb806c21, not stripped
$ ./helloworld
Hello World!
Note that networking support is fully functional so we're now working on an emulated ARM64 environment on our hosts PC as we were on the ESPRESSObin.

See also