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.
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).
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 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.
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).
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.
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
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.