Building kernel modules for the Nokia N800/N810

Copyright 2007, 2008 Aleksandr Koltsoff <czr@iki.fi>

This step-by-step guide describes the steps necessary to build out-of-tree modules for the Nokia N800 and the Nokia N810. It mainly covers OS2007/Bora but includes a section on changes required for Chinook/OS2008 (N810) at the end.

The command listings also include the error messages that you might receive if you assume that the kernel (and kernel header) packaging works in maemo SDK the same way as it works in normal Linux distributions. This is to aid search engines so that users experiencing these problems would find this page.

Table of contents

Pre-requisites

In order to build anything for the device, you will need to install the suitable version of the maemo SDK on your development machine. This guide only covers version 3.2 (and has not been tested with other versions, or other devices). Please see the official guides on instructions how to install the SDK.

You will also need the module source code that you wish to build. This guide includes a very simple "Hello World"-style module which implements the bare necessary functions to print out a message to the kernel log (which you may view with dmesg on the device itself).

In order to test the module, you will need to access your device as root user. The easiest way for this is to install an ssh server on the device, and login as root user (the password is rootme). Installing the ssh server package is not covered here (please see the official site for further details).

Re-flashing the device is not required. Neither is enabling the "R&D"-mode.

Testing the hello world module is done with the insmod command, in the regular way (and is not covered in this document).

Preparing the SDK

Normally one would start by installing the relevant kernel-headers package, and it would contain all the necessary components for the out-of-tree Kbuild system to work. Alas, this is not the case with maemo SDK.

We start by making sure that we've switched to an target that can generate binary code compatible with the device. We also install the kernel header package. (The package might already be installed for you.)

 1  [sbox-SDK_X86: ~] > sb-conf select SDK_ARMEL
 2  Shell restarting...
 3  [sbox-SDK_ARMEL: ~] > fakeroot apt-get install rx-34-kernel-headers
 4  Reading Package Lists... Done
 5  Building Dependency Tree... Done
 6  The following NEW packages will be installed:
 7    rx-34-kernel-headers
 8  0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded.
 9  Need to get 0B/2854kB of archives.
10  After unpacking 17.9MB of additional disk space will be used.
11  Selecting previously deselected package rx-34-kernel-headers.
12  (Reading database ... 15662 files and directories currently installed.)
13  Unpacking rx-34-kernel-headers (from .../rx-34-kernel-headers_2.6.18-200724osso1_all.deb) ...
14  Setting up rx-34-kernel-headers (2.6.18-200724osso1) ...
15  [sbox-SDK_ARMEL: ~] > ls -la /usr/src/rx-34-kernel-headers
16  total 92
17  drwxr-xr-x   4 user user  4096 Oct  6 17:37 .
18  drwxrwxr-x   4 user user  4096 Oct  6 17:37 ..
19  -rw-r--r--   1 user user 27687 Jun 13 11:33 .config
20  -rw-r--r--   1 user user 47794 Nov  8  2006 Makefile
21  drwxr-xr-x   3 user user  4096 Oct  6 17:37 arch
22  drwxr-xr-x  18 user user  4096 Oct  6 17:37 include

Listing 1: Switching target to ARMEL, and installing the kernel header package

Everything looks good. Except that the last ls listing is deceptively short. Normally one would expect slightly more directories.

Assuming normal Linux distribution

Assuming that this would be a normal Linux distribution, we proceed by attempting to build the module. Note that since we're not running the kernel that we're building against, we need to tell the where the kernel module support code is rooted at. The provided Makefile includes this functionality.

1  [sbox-SDK_ARMEL: ~] > cd hello-n800
2  [sbox-SDK_ARMEL: ~/hello-n800] > KERNELDIR=/usr/src/rx-34-kernel-headers make
3  make -C /usr/src/rx-34-kernel-headers M=/home/user/hello-n800 modules
4  make[1]: Entering directory `/targets/SDK_ARMEL/usr/src/rx-34-kernel-headers'
5  Makefile:273: /targets/SDK_ARMEL/usr/src/rx-34-kernel-headers/scripts/Kbuild.include: No such file or directory
6  Makefile:490: /targets/SDK_ARMEL/usr/src/rx-34-kernel-headers/arch/arm/Makefile: No such file or directory
7  make[1]: *** No rule to make target `/targets/SDK_ARMEL/usr/src/rx-34-kernel-headers/arch/arm/Makefile'.  Stop.
8  make[1]: Leaving directory `/targets/SDK_ARMEL/usr/src/rx-34-kernel-headers'
9  make: *** [modules] Error 2

Listing 2: Trying to build hello for the first time

That didn't go all too well.

Preparing the module build environment manually

Since by now it's fairly obvious that we cannot use the kernel headers package to build modules, we next decide to recreate a proper tree that is suitable for module building. The kernel Makefile has a special target for this (called 'prepare').

We'll start by installing the source code package for the kernel and then uncompress it.

 1  [sbox-SDK_ARMEL: ~/hello-n800] > apt-cache search kernel-source
 2  kernel-source-rx-34 - Linux kernel sources for rx-34 product
 3  [sbox-SDK_ARMEL: ~/hello-n800] > fakeroot apt-get install kernel-source-rx-34
 4  Reading Package Lists... Done
 5  Building Dependency Tree... Done
 6  Suggested packages:
 7    kernel-package libqt3-dev
 8  The following NEW packages will be installed:
 9    kernel-source-rx-34
10  0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded.
11  Need to get 0B/42.6MB of archives.
12  After unpacking 42.7MB of additional disk space will be used.
13  Selecting previously deselected package kernel-source-rx-34.
14  (Reading database ... 18412 files and directories currently installed.)
15  Unpacking kernel-source-rx-34 (from .../kernel-source-rx-34_2.6.18-osso52_all.deb) ...
16  Setting up kernel-source-rx-34 (2.6.18-osso52) ...
17  [sbox-SDK_ARMEL: ~/hello-n800] > dpkg -L kernel-source-rx-34
18  /.
19  /usr
20  /usr/src
21  /usr/src/kernel-source-rx-34.tar.bz2
22  /usr/share
23  /usr/share/doc
24  /usr/share/doc/kernel-source-rx-34
25  /usr/share/doc/kernel-source-rx-34/README.Debian
26  /usr/share/doc/kernel-source-rx-34/copyright
27  /usr/share/doc/kernel-source-rx-34/changelog.Debian.gz
28  [sbox-SDK_ARMEL: ~/hello-n800] > cd /usr/src
29  [sbox-SDK_ARMEL: /usr/src] > tar xjf kernel-source-rx-34.tar.bz2
30  [sbox-SDK_ARMEL: /usr/src] > du -s kernel-source-rx-34
31  277200  kernel-source-rx-34
32  [sbox-SDK_ARMEL: /usr/src] > ls -lad kernel-source-rx-34
33  drwxr-xr-x  19 user user 4096 Jun 13 11:25 kernel-source-rx-34
34  [sbox-SDK_ARMEL: /usr/src] > cd ~/hello-n800

Listing 3: Installing the kernel source package

Satisfied with the relative size of the source tree (it might actually contain source code) we proceed by configuring the kernel, and then executing the preparation step. After this we attempt at building the hello module again.

 1  [sbox-SDK_ARMEL: ~/hello-n800] > pushd /usr/src/kernel-source-rx-34
 2  /usr/src/kernel-source-rx-34 ~/hello-n800
 3  [sbox-SDK_ARMEL: /usr/src/kernel-source-rx-34] > cp ../rx-34-kernel-headers/.config .
 4  [sbox-SDK_ARMEL: /usr/src/kernel-source-rx-34] > make oldconfig
 5  *
 6  * Linux Kernel Configuration
 7  *
 8  *
 9  * Code maturity level options
10  *
11  Prompt for development and/or incomplete code/drivers (EXPERIMENTAL) [Y/n/?] y
12  ......
13  *
14  * Library routines
15  *
16  CRC-CCITT functions (CRC_CCITT) [Y/?] y
17  CRC16 functions (CRC16) [N/m/y/?] n
18  CRC32 functions (CRC32) [Y/?] y
19  CRC32c (Castagnoli, et al) Cyclic Redundancy-Check (LIBCRC32C) [N/m/y/?] n
20  #
21  # configuration written to .config
22  #
23  [sbox-SDK_ARMEL: /usr/src/kernel-source-rx-34] > make prepare
24  scripts/kconfig/conf -s arch/arm/Kconfig
25  arch/arm/mach-lh7a40x/Kconfig:17:warning: 'select' used by config symbol 'MACH_LPD7A400' refer to undefined symbol 'HAS_TOUCHSCREEN_ADS7843_LH7'
26  arch/arm/mach-lh7a40x/Kconfig:27:warning: 'select' used by config symbol 'MACH_LPD7A404' refer to undefined symbol 'HAS_TOUCHSCREEN_ADC_LH7'
27    CHK     include/linux/version.h
28    UPD     include/linux/version.h
29    SYMLINK include/asm-arm/arch -> include/asm-arm/arch-omap
30    Generating include/asm-arm/mach-types.h
31    CHK     include/linux/utsrelease.h
32    UPD     include/linux/utsrelease.h
33    SYMLINK include/asm -> include/asm-arm
34    CC      arch/arm/kernel/asm-offsets.s
35    GEN     include/asm-arm/asm-offsets.h
36  [sbox-SDK_ARMEL: /usr/src/kernel-source-rx-34] > popd
37  ~/hello-n800
38  [sbox-SDK_ARMEL: ~/hello-n800] > KERNELDIR=/usr/src/kernel-source-rx-34 make
39  make -C /usr/src/kernel-source-rx-34 M=/home/user/hello-n800 modules
40  make[1]: Entering directory `/targets/SDK_ARMEL/usr/src/kernel-source-rx-34'
41  
42    WARNING: Symbol version dump /targets/SDK_ARMEL/usr/src/kernel-source-rx-34/Module.symvers
43             is missing; modules will have no dependencies and modversions.
44  
45    CC [M]  /home/user/hello-n800/hello.o
46    Building modules, stage 2.
47    MODPOST
48  /scratchbox/tools/bin/sh: line 1: scripts/mod/modpost: No such file or directory
49  make[2]: *** [__modpost] Error 127
50  make[1]: *** [modules] Error 2
51  make[1]: Leaving directory `/targets/SDK_ARMEL/usr/src/kernel-source-rx-34'
52  make: *** [modules] Error 2

Listing 4: Creating a minimal out-of-tree module support infrastructure

At this point we now know that we only have one option left. Since the preparation step doesn't yield a working environment (for unknown reasons), we proceed with full kernel build. The build will take some time, but compared to desktop kernels, not much.

After the kernel has been built, we try to build our module again.

You might have noticed by now that we haven't touched the module source even once (and we shouldn't have, since the module itself works).

 1  [sbox-SDK_ARMEL: ~/hello-n800] > !pushd
 2  pushd /usr/src/kernel-source-rx-34
 3  /usr/src/kernel-source-rx-34 ~/hello-n800
 4  [sbox-SDK_ARMEL: /usr/src/kernel-source-rx-34] > make
 5    CHK     include/linux/version.h
 6  make[1]: `include/asm-arm/mach-types.h' is up to date.
 7    CHK     include/linux/utsrelease.h
 8    CC      scripts/mod/empty.o
 9    HOSTCC  scripts/mod/mk_elfconfig
10  ......
11    LD [M]  fs/ext3/ext3.ko
12    CC      fs/jbd/jbd.mod.o
13    LD [M]  fs/jbd/jbd.ko
14    CC      fs/mbcache.mod.o
15    LD [M]  fs/mbcache.ko
16  [sbox-SDK_ARMEL: /usr/src/kernel-source-rx-34] > popd
17  ~/hello-n800
18  [sbox-SDK_ARMEL: ~/hello-n800] > !KERN
19  KERNELDIR=/usr/src/kernel-source-rx-34 make
20  make -C /usr/src/kernel-source-rx-34 M=/home/user/hello-n800 modules
21  make[1]: Entering directory `/targets/SDK_ARMEL/usr/src/kernel-source-rx-34'
22    Building modules, stage 2.
23    MODPOST
24    CC      /home/user/hello-n800/hello.mod.o
25    LD [M]  /home/user/hello-n800/hello.ko
26  make[1]: Leaving directory `/targets/SDK_ARMEL/usr/src/kernel-source-rx-34'
27  [sbox-SDK_ARMEL: ~/hello-n800] > ls -la *.ko
28  -rw-rw-r--  1 user user 2130 Oct  6 18:04 hello.ko
29  [sbox-SDK_ARMEL: ~/hello-n800] > file hello.ko
30  hello.ko: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped

Listing 5: Full kernel build, and final module build

For Great Victory!

What is left now is copying the driver to the device (using scp) and then running insmod hello.ko in the device. After that you may use dmesg to see whether it will contain the message from the module (it should).

Changes for Chinook/OS2008/N810

The overall process is very similar to the process used with Bora/OS2007/N800.

Getting root access on OS2008 is different from what it used to be (please see the official site for instructions). R&D mode might be required (depending on how you decide to get root access).

The kernel source is no longer packaged as a separate binary package, but instead lives in source package format only (so it's no longer possible to find it with apt-cache). There is no longer a kernel header package either (it didn't work before anyway).

The source package name for the kernel is (as of this writing) kernel-source-rx-34. N810 is also known as RX-44, but N810 and N800 are hardware compatible with respect to the kernel build, so the same package will work on both (hence, the package name contains rx-34).

The prepare target in the kernel Makefile will yield an incomplete configuration (same as before), so a full kernel build is unfortunately still required.

Below is a capture of getting the kernel source package, building the kernel and then building the hello world module. The unnecessary steps which were present for N800 examples are omitted.

 1  [sbox-CHINOOK_ARMEL: ~] > mkdir src
 2  [sbox-CHINOOK_ARMEL: ~] > cd src
 3  [sbox-CHINOOK_ARMEL: ~/src] > apt-get source kernel-source-rx-34
 4  ..(The package is about 55 MiB, so it takes a while)..
 5  [sbox-CHINOOK_ARMEL: ~/src] > cd kernel-source-rx-34-2.6.21.0
 6  [sbox-CHINOOK_ARMEL: ~/src/kernel-source-rx-34-2.6.21.0] > make nokia_2420_defconfig
 7    HOSTCC  scripts/basic/fixdep
 8    HOSTCC  scripts/basic/docproc
 9    HOSTCC  scripts/kconfig/conf.o
10  ... Followed by the result of running oldconfig ...
11  *
12  * Library routines
13  *
14  CRC-CCITT functions (CRC_CCITT) [Y/?] y
15  CRC16 functions (CRC16) [N/m/y/?] n
16  CRC32 functions (CRC32) [Y/?] y
17  CRC32c (Castagnoli, et al) Cyclic Redundancy-Check (LIBCRC32C) [N/m/y/?] n
18  #
19  # configuration written to .config
20  #
21  [sbox-CHINOOK_ARMEL: ~/src/kernel-source-rx-34-2.6.21.0] > make
22  scripts/kconfig/conf -s arch/arm/Kconfig
23    CHK     include/linux/version.h
24    UPD     include/linux/version.h
25  ... Kernel builds ...
26    CC      fs/mbcache.mod.o
27    LD [M]  fs/mbcache.ko
28  [sbox-CHINOOK_ARMEL: ~/src/kernel-source-rx-34-2.6.21.0] > cd ~/hello-n800-0.1
29  [sbox-CHINOOK_ARMEL: ~/hello-n800-0.1] > KERNELDIR=~/src/kernel-source-rx-34-2.6.21.0 make
30  make -C /home/user/src/kernel-source-rx-34-2.6.21.0 M=/home/user/hello-n800-0.1 modules
31  make[1]: Entering directory `/home/user/src/kernel-source-rx-34-2.6.21.0'
32    CC [M]  /home/user/hello-n800-0.1/hello.o
33    Building modules, stage 2.
34    MODPOST 1 modules
35    CC      /home/user/hello-n800-0.1/hello.mod.o
36    LD [M]  /home/user/hello-n800-0.1/hello.ko
37  make[1]: Leaving directory `/home/user/src/kernel-source-rx-34-2.6.21.0'
38  [sbox-CHINOOK_ARMEL: ~/hello-n800-0.1] > file hello.ko
39  hello.ko: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped

Listing 6: Retrieving and building the kernel for OS2008/N810 and building the example module

You might be wondering about the nokia_2420_defconfig kernel build target. It is a kernel configuration file that lives in arch/arm/configs/nokia_2420_defconfig file. You can also copy this file into the kernel source directory and rename it to .config and then just run make without any target names (like was done for the N800 example). In the above capture, the config name was used directly (because the official maemo 4.0 kernel how-to also uses it).

After copying the hello.ko to N810, one can next test it out with the regular module insertion and removal commands:

1  Nokia-N810-42-18:~# insmod hello.ko; rmmod hello.ko; dmesg | tail -2
2  [ 2235.476562] Hello, world
3  [ 2235.492187] Goodbye, cruel world

Listing 7: Testing the module on N810

There is an official kernel how-to document available at http://maemo.org/development/documentation/how-tos/4-x/kernel_guide_for_maemo.html. The how-to does not cover out-of-tree module building.

Source code for the hello world module

The simplest possible Hello World. The source code is provided for your convenience only (it is also included within the release tarball).

 1 /**
 2  * A very simple kernel module with just the basics.
 3  * (doesn't do anything useful, but doesn't crash the kernel either)
 4  */
 5 
 6 #include <linux/init.h>
 7 #include <linux/module.h>
 8 
 9 // Special macro to indicate license (to avoid tainting the kernel)
10 // One some specific strings are recognized by the kernel as
11 // "compatible with GPL". Otherwise the result will be a tainted
12 // kernel.
13 MODULE_LICENSE("GPLv2");
14 
15 /**
16  * Our module initialization function
17  *
18  * PARAMETERS:
19  * None
20  *
21  * RETURNS:
22  * 0: success
23  * !0: error code on failures
24  */
25 static int hello_init(void) {
26   // KERN_ALERT is the highest priority message from kernel.
27   // NOTE: there is no comma after it KERN_ALERT
28   printk(KERN_ALERT "Hello, world\n");
29   return 0;
30 }
31 
32 /**
33  * Our module deinitialization function
34  *
35  * PARAMETERS:
36  * None
37  *
38  * RETURNS:
39  * void
40  */
41 static void hello_exit(void) {
42   // printk operates as normal libc printf, but is not as flexible.
43   // Not all GNU extensions are supported and particularly floating
44   // point support is not there (for a good reason).
45   printk(KERN_ALERT "Goodbye, cruel world\n");
46 }
47 
48 // macro to mark the module initialization code (run on insmod)
49 module_init(hello_init);
50 // macro to mark the finalization code (run on rmmod)
51 module_exit(hello_exit);

Listing 8: Source of temp/hello.c

Obtaining the source code

The release tarball also includes the Makefile which is necessary to build the module.

For release tarballs please consult the release directory. The most recent changelog is also included there.