Raspberry Pi Kernel Modules development

Once, obtaining the header files needed for our module to be compiled with the present kernel was a complicated exercise with cross-referencing several places. Then a tool for automating all this was made, but now finally it has been realized that this is so useful, that there is a package for it. To put this into place, do:

apt install raspberrypi-kernel-headers

Then make sure there is a reference to these made, via your .profile or similar file:

export KERNEL_SRC=/usr/src/linux-headers-`uname -r` 

Other useful things is the manual pages for kernel functions (section 9):

apt install linux-manual-3.18

(apt list | grep linux-manual will help finding the latest one.)

As noted below also, I like to call the makefile for these kernel modules Kbuild, and leave the Makefile name for user-space code. With the macros above, these Kbuild files become just something like this one for a kernel module called accumulator, whose source is in the file accumulator.c and will become the kernel module file accumulator.ko:


MODULE=accumulator

obj-m:= ${MODULE}.o

all:
        ARCH=arm CROSS_COMPILE=${CCPREFIX} make -C ${KERNEL_SRC} M=$(PWD) modules

clean:
        ARCH=arm CROSS_COMPILE=${CCPREFIX} make -C ${KERNEL_SRC} M=$(PWD) clean


Then invoke this with make -f Kbuild and there we go.

When loaded, the parameters it has shows up in /sys/module/accumulator and it can also support /proc entries.

Examples

Read Thumbwheel switches

Here is a simple, read-only character driver that looks at three gpios and returns the level combination as a number from 0 through 7, as driven from a thumbwheel switch where the common is returned to 0V and there are pull-up resistors for each line, so voltage level L means 1 and H means 0.

GPIO17- wire 1 bit 0 
GPIO27- wire 2 bit 1 
GPIO22- wire 4 bit 2

The tarball, dsrc-rtws-181114.tar.gz , contains the C file and the Kbuild. The resulting /dev/rtws device-file shows up as readable by all.

IO to another device

I also have another example here, stn41-device-160102.tar.gz , of device-driver code for a device that talks with another processor via a few GPIOs and some shift-registers. This is a misc-device and uses /proc entries for control. Writing to the device makes it send data out, and reading the device pulls data in. Of course the microprocessor and its hardware will have to be present on the other side for this to work.

How fast can we go?

For finding out how fast I could clock the PI on one of the interrupting GPIOs, notably from a wind-speed sensor, and whether it would be fast enough to capture each 2/3 meter pulse, I have made a driver, code in dsrc-windvol-181001.tar.gz, that receives these on GPIO22 (Pin 15) and sends a signal to a user process for each of these -- then connect a function-generator to the gpio22 and crank up the frequency and see what happens...

Earlier state of affairs

All the rest of the page is some historical info on what we had to do before to get the basics in place.

Originally, this turned out to be non-obvious, but there is now a tool called rpi-source that can do all the hard work for us.

We can pull this from https://github.com/notro/rpi-source

Extract the executable file (it is a Python script) and place it in /usr/bin

Then make sure bc and ncurses are in place:

sudo apt-get install bc libncurses5-dev

Then after this, it can be run something like:

rpi-source -d /home/rpicom/kernel

Put the corresponding location into .profile so we can make the modules with the Kbuild files detailed below. You will have to log-out and log back in for this to become turned on:

KERNEL_SRC=/home/rpicom/kernel/linux

The path here should correspond to the location where the kernel source was located with the -d argument to rpi-source.

Once having run the rpi-source command, the necessary support files for making the modules are in place, as per section 6 below.

The old way

It wasn't always this simple. An alternate way that still works before the discovery of the rpi-source program, and also cross-compiling is discussed below.

From various places on the web, such as blogs and forums, I have managed to do the Cross-compiling the 3.2.27+ and 3.6.11+ kernels for the Raspberry Pi, in order to be able to make kernel modules working, as follows.

0.

Unpack the tools tarball in /opt so it all ends up under /opt/r-pi-tools-master

Also make sure the git utility is in place. When doing this on a host Debian system, the command

apt-get install git

will do the job.

If self-compiling, you'll need bc, apt-get install bc will do. For the make menuconfig, ncurses will be needed as well, apt-get install ncurses will pull in the needed items.

1.

Define these environment variables:

MODULES_TEMP=/home/rpicom/kernel/modules
KERNEL_SRC=/home/rpicom/kernel/linux
CCPREFIX=/opt/r-pi-tools-master/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin/arm-bcm2708-linux-gnueabi-

Note that the CCPREFIX ends in a dash, this prefix apply to the cc, ar, ld, and other tools all located in the indicated directory.

I have also made a separate user in the development system for this kind of work, rpicom, and stuck these lines into the .profile file for this, as follows:

export MODULES_TEMP=/home/rpicom/kernel/modules
export KERNEL_SRC=/home/rpicom/kernel/linux
export CCPREFIX=/opt/tools-master/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin/arm-bcm2708-linux-gnueabi-

For self-development (compiling the kernel on a Raspberry Pi itself) I used the environment variables set in the /home/rpicom/.profile as follows:

export MODULES_TEMP=/home/rpicom/kernel/modules
export KERNEL_SRC=/home/rpicom/kernel/linux

There is no need for the CCPREFIX here, as we're self-compiling.

Note that the Model A doesn't have enough memory for the git extraction to be able to run. The kernel source has to be pulled down elsewhere and copied across.

2.

Kernel 3.2.27+

Get the appropriate kernel source. This isn't just as easy as it would seem. The standard kernel source tarball has the version 3.2.27, the installed kernel has 3.2.27+ and it has to be pulled out as the exact snapshot from the git repository, using the referenced revision identifiers:

git clone git://github.com/raspberrypi/linux.git
cd linux
git checkout 10182a3bc434b27740f81c2b836a1af943060241

The identifier in the checkout command is the contents of the correct git-checkin for the branch. To figure this correct one out (as per http://www.raspberrypi.org/phpBB3/viewtopic.php?f=66&t=27413&hilit=palaver ) first, look in

/usr/share/doc/raspberrypi-bootloader/changelog.debian.gz

for the latest changelog entry, there will be a 10-digit hex number there, I had 17c8799375. Then go to https://github.com/raspberrypi/firmware/commit/17c8799375 and click on [browse code]. Look for the file extra/git_hash and inside this there is the commit ID, which is the argument to the git checkout command shown above:

10182a3bc434b27740f81c2b836a1af943060241

Kernel 3.6.11+

In March 2013, the apt-get upgrade changed the kernel, so it is now 3.6.11+. The logic should be similar:

/usr/share/doc/raspberrypi-bootloader/changelog.debian.gz

now indicates that the latest change number is 5dd9b4962e. Hence I go to https://github.com/raspberrypi/firmware/commit/5dd9b4962e and click on the Files tab. Looking for the file extra/git_hash and inside there is the correct kernel commit ID:

79ec5aa57e2466e2cad5395fb47ad9c5c42537f9

Then the extraction-commands are similar:

git clone git://github.com/raspberrypi/linux.git
cd linux
git checkout 79ec5aa57e2466e2cad5395fb47ad9c5c42537f9

Presumably this change in the kernel has to do with the camera module being launched.

I expect to be able to handle further different kernel versions the same.

Kernel 3.6.11+ 2013-09-29

During the summer, the kernel was updated further. As it keeps being updated via apt-get upgrade, the system for compiling and linking kernel modules have to be upgraded as well.

Now the /usr/share/doc/raspberrypi-bootloader/changelog.debian.gz says that the change number is

d4f5315cfac4e. Hence go look to https://github.com/raspberrypi/firmware/commit/d4f5315cfac4e and click on the Files tab. Looking for the file extra/git_hash and inside there is the kernel commit ID:

966efc7d993edb91b5be3886e89df2b90f29ee50

Then the extraction-commands are similar:

git clone git://github.com/raspberrypi/linux.git
cd linux
git checkout 966efc7d993edb91b5be3886e89df2b90f29ee50

Kernel 3.10.28+ 2014-01-28

As before, git commit id was found to be 98e3be3a7e and so go look at http://github.com/raspberrypi/firmware/commit/98e3be3a7e .

From looking at the contents of the file extra/git_hash we then find the commit ID to be :

76d183fbc25004148d3f844a8c78c7cbc59dd1f0

Then it is the matter of performing:

git clone git://github.com/raspberrypi/linux.git
cd linux
git checkout 76d183fbc25004148d3f844a8c78c7cbc59dd1f0

Kernel 3.10.25+ 2014-01-28

This seems to be the one that I get around mid-March 2014, when doing the rpi-update. The firmware is 030082b403b and the commitid is now 16eb921a96db3dc3555a65b53b99c15753e6b770

Then it is the matter of performing:

git clone git://github.com/raspberrypi/linux.git
cd linux
git checkout 16eb921a96db3dc3555a65b53b99c15753e6b770

Kernel 3.12.28+ 2014-09-08

Changelog.Debian had: 53d1ae311226b5c

This translates to git commit id: 94a382fed1a5ec303eef9e1f3439ddf8f759ba60c

Kernel 4.1.6+ 2015-08-18

Changelog.Debian had: bbf5495c0424fe38d795111007e93f8763b88bcc

So we then go look at http://github.com/raspberrypi/firmware/commit/bbf5495c0424fe38d795111007e93f8763b88bcc.

Once there, find the file raspberrypi/firmware/extra/git_hash

This contains to git commit id: 4507c9752292506fa6ef136114ad14ffd92b2ca5

Finally, get the kernel source:

git clone git://github.com/raspberrypi/linux.git
cd linux
git checkout 4507c9752292506fa6ef136114ad14ffd92b2ca5

Kernel 4.1.13-v7+ 2015-11-13 (Jessie)

The contents of

/usr/share/doc/raspberrypi-bootloader/changelog.debian.gz

showed

 * firmware as of 2a329e0c7d8ea19c085bac5633aa4fccee0f21be 

And so we go to look up http://github.com/raspberrypi/firmware/commit/2a329e0c7d8ea19c085bac5633aa4fccee0f21be. From the [Browse files] button there, we go look for extra/git_hash, and inside this is found:

bc1669c846b629cface0aaa367afb2b9c6226faf

Thus the extraction commands become:

git clone git://github.com/raspberrypi/linux.git
cd linux
git checkout bc1669c846b629cface0aaa367afb2b9c6226faf

Another variation

For the latest, look at https://github.com/raspberrypi/firmware and look at the extra/git_hash file there.

3.

Pull /proc/config.gz from the R.Pi file system.

On kernels version 4 onwards, this does not exist unless the module called configs is in place. To make the /proc/config.gz available use modprobe configs to make it appear.

Unpack the config.gz file as config, then you may edit it if you know about a certain setting that you want to change, such as CONFIG_I2C_GPIO=m. (this particular one actually showed up as having been turned on in the 4.1.13-v7+ kernel). Once satisfied with that, copy this file to the linux directory as .config, after having cleaned out any old compilation results. I had this file sitting in the parent directory of the linux directory:

cd linux
make mrproper 
cp ../config .config
ARCH=arm CROSS_COMPILE=${CCPREFIX} make oldconfig

The ARCH=...{CCPREFIX} stuff isn't needed when compiling on the Raspberry Pi itself, then it is just make oldconfig.

However, while cross-compiling make sure the initial ARCH= is in all capitals, or the system will think it is some weird and horrible mix of the host computer's X86 and the ARM, and the configuration will not work!

if needing to change things in the configuration, do:

	ARCH=arm CROSS_COMPILE=${CCPREFIX} make menuconfig

This requires ncurses, apt-get install ncurses, in order to work.

I did not need to do all this just now, after all, the goal of this exercise is to make compatible kernel modules. However, when wanting to re-do the kernel with the bit-banging i2c-gpio module added, then compile and link the i2c-gpio-param module, as described on the page on i2c.

4.

Make everything. The first of these make-commands takes several hours. on the small HP Mini machine I used. It also took a while for 3.6.11 on another 1 GHz machine that I used, that ran Debian Squeeze, but nowhere near as long as on the smaller portable. I have also compiled kernels on the Raspberry Pi Model B and B+ itself. That took nearly 11 hours for the make command and about 10 minutes for the make modules command. (make started at 17:01 finished at 03:57, make modules started at 07:00 and finished at 07.10). This was with a class 4 32 GB SD card from Sandisk. It might be useful to set-up a separate Raspberry Pi, and preferrably one of the four-core 2 B models, for these kinds of exercises, one with an external hard disk for the heavy lifting (using a small SD card for boot and the disk for / should work).

ARCH=arm CROSS_COMPILE=${CCPREFIX} make 
ARCH=arm CROSS_COMPILE=${CCPREFIX} make modules

For self-compiling, we can do without the preamble and the commands are thus simply:

make 
make modules

The cross-compiling seems to not work on slightly older systems, though it did work on Fedora 16 and on Debian Squeeze, but not on an older Lenny version of Debian.

I also had problems getting this to work on a 64-bit machine, seems the toolchain is only set-up for 32-bit x86 systems as it comes out of the box. Similar problems that I found with the toolchains for Picotux, the White-light WD Worldbook, and the wrt54gl, were solved by including the package lib32z1 on the 64-bit X86 host:

apt-get install lib32z1

So cross-compiling is fraught with a number of problems, none of which apply when doing the self-compiling: compile the kernel on the Raspbery Pi itself.

Self-compiling works fine on the Raspberry B, B+ and 2B with 512MB or more memory. The A and A+ models with their 256M run out of space in the process and fail to complete. So any kernel changes for these will have to be done on another machine. Once the kernel is finished, our own modules can still be made on the A and A+ however; there is still room for that.

After all of this, we finally have the right modules and the all-important file Modules.symvers that we need for compiling our own modules later on. We also have the new kernel in the file kernel/linux/arch/arm/boot/zImage

5.

Now if we care about the rest of them, we can package the modules, which end up in where our defined MODULES_TEMP is. If this is a location that we can't write to as ourselves, we have to be root.

ARCH=arm CROSS_COMPILE=${CCPREFIX} INSTALL_MOD_PATH=${MODULES_TEMP} make modules_install

or something like

INSTALL_MOD_PATH=/home/rpicom/kernel/modules make modules_install

for compiling on the Raspberry Pi itself. To put the modules into the proper location under /lib, do (and now we have to be root)

make modules_install

Similarly, if new kernel is of interest, the kernel file zImage can be copied into /boot as /boot/kernel_xyz.img or something similar; an alternate kernel, in /boot/kernel_alt.img, can be referenced in the file /boot/config.txt as kernel=kernel_alt.img.

When this is done, reboot and fingers crossed. If there is an error made here, the machine might not be able to start, so now the arrangement with SD cards that can be pulled and read and written somewhere else turns out really useful.

6.

Finally, I can try compiling my own module source:

There has to be a file named Module.symvers ready in the kernel source tree, as created in number 4 above.

kernel-source tree is in /home/rpicom/kernel/linux.
M= is the directory where the source .c files for the module are. usually `pwd` or this same directory here.

cd /home/rpicom/dsrc/lindd

Then do

ARCH=arm CROSS_COMPILE=${CCPREFIX} make -C /home/rpicom/kernel/linux M=/home/rpicom/dsrc/lindd modules

or, utilizing the environment-variables that were set earlier:

ARCH=arm CROSS_COMPILE=${CCPREFIX} make -C ${KERNEL_SRC} M=${PWD} modules

I then tried out the simple hello.c module from http://www.freesoftwaremagazine.com/articles/drivers_linux?page=0%2C11

I made a hello.ko module file that was accepted into the kernel, saying hello when loaded and goodbye when unloaded.

Next is to manipulate GPIO lines, DMAs and other things that may be of interest.

I like to call the makefile for these kernel modules Kbuild, and leave the Makefile name for user-space code. With the macros above, these Kbuild files become just something like this one for a kernel module called accumulator, whose source is in the file accumulator.c and will become the kernel module file accumulator.ko:


MODULE=accumulator

obj-m:= ${MODULE}.o

all:
        ARCH=arm CROSS_COMPILE=${CCPREFIX} make -C ${KERNEL_SRC} M=$(PWD) modules

clean:
        ARCH=arm CROSS_COMPILE=${CCPREFIX} make -C ${KERNEL_SRC} M=$(PWD) clean


Then invoke this with make -f Kbuild and there we go.

When loaded, the parameters it has shows up in /sys/module/accumulator and it can also support /proc entries.

Examples

I have an example here of device-driver code for a device that talks with another processor via a few GPIOs and some shift-registers. This is a misc-device and uses /proc entries for control. Writing to the device makes it send data out, and reading the device pulls data in. Of course the microprocessor and its hardware will have to be present on the other side for this to work.