How-to Write a Kernel Module for Android

In this article I will describe how you can write a kernel module for Android or its after market product like CyanogenMod. As we all know Android is based upon and using the GNU/Linux kernel. Therefore, writing/building kernel modules for android is same as writing/building a kernel module for the common Linux found on our desktop or server machines. However, if you ever try to build kernel module for android using the guidelines written for the desktop Linux machine you could possibly not be able do build it. According to my knowledge and experience, the reason behind that possible module build failure is the integrated build environment of Android Open Source Project (AOSP).  In order to write and build a kernel module for android you need to first download the android source. In this tutorial I am using CyanogenMod 11 but you can use also the android source from the AOSP. Moreover, I am using the AOSP android emulator for demonstrating this tutorial. However, I have tested this exact method on CyanogenMod 10 on a Samsung Galaxy S-II i9100G. So my guess is it would work for any device. Also if you are using your phone for this experiment please backup everything and don’t blame me if you phone is bricked. Before starting make sure you have all the prerequisite libraries on your system please see the AOSP or CyanogenMod website for this.

Step 1:

Download the AOSP or CyanogenMod Source (If you already have a downloaded source leave this step).


$ mkdir cm11
$ cd cm11
$ repo init -u git://github.com/CyanogenMod/android.git -b cm-11.0
$ repo sync

It would take a couple of hours to download and maybe more if you are behind a low bandwidth. Also if you are using emulator for this experiment do not download cm-10.x as it is not downloading the Goldfish kernel 2.6.xxxx source (I don’t why but it happens to me, maybe it is exception i.e. special case).

Step 2:

Once you have  source downloaded you have to breakfast or lunch;so that, the kernel sources for your device are downloaded. Also please check your device specific build guides in case you are using CyanogenMod in order to retrieve pre-builts and proprietary blobs.


$ . build/envsetup.sh
$ lunch

The lunch command will list the devices for which CyanogenMod can be built as shown in the image below:
Kernel-Build-1-Screenshot - 07312015 - 07_47_29 PM

As I am building for the emulator, I have selected the option 6, which is the cm_goldfish-eng. Instead of the lunch command, you can also use the breakfast command which takes the device code (these device codes can be found here) name as a parameter. So in our case it would be:


$ breakfast cm_goldfish-eng

After the lunch or breakfast command (whichever you have used) it take a couple of minutes to download the kernel sources for your device in our example it will download the Goldfish Kernel (3.4.xxx).

Step 3:

Once the kernel source is downloaded, than we are good to go write and build our first kernel module. In order to write your module follow the following steps:

a) First goto the kernel source directory. For our example it is in kernel/goldfish/ directory. From the AOSP/CyanogenMod so:

$ cd kernel/goldfish

b) Once you are in the kernel source folder. We will create a directory for our module in the drivers directory of the kernel source. So technically we are building a driver which will print hello world on kernel message buffer. In order to do so go to the drivers directory and create a directory for your module.

$ cd drivers
$ mkdir helloworld
$ cd helloworld

c) Now here create/write your module files. For the example I have created a hello.c file with the following contents:


#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Abdullah Yousafzai, 2015");
MODULE_DESCRIPTION("Hello World Kernel Module for Android");

static int hello_init(void)
{
    printk("Hello Kernel World....\n");
	return 0;
}
static void hello_exit(void)
{
    printk("Goodbye Kernel World\n");
}
module_init(hello_init);
module_exit(hello_exit);

d) Now create the KBuild system Kconfig file in the module directory. I have created the Kconfig file with the following contents for this example.


config HELLO_MOD
	tristate "Hello World Module"
	default m
	depends on MODULES
	help
	  This module will write Hello Kernel World to the Kernel Message Buffer

e) Once this is done now create the module Makefile. I have created the Makefile with the following contents:


ifneq ($(KERNELRELEASE),)
# call from kernel build system
obj-m	:= hello.o
endif

Now our module is written but now in the coming steps we have to change certain configuration files in order to make that module available in the Android ROM/Emulator you are building.

Step 4:

Once the module is written with all the basic necessary source file, Kconfig and Makefile. We have to add refrence to these two files in the main drivers Kconfig and Makefile. In order to do so go to the drivers directory and open the Kconfig and Makefile in the editor of your choice and follow the following steps.

a) After opening the drivers/Kconfig add the following statement in the end of the file before the endmenu line. This shown in the picture also.

source "drivers/helloworld/Kconfig"


b) Once the drivers/Kconfig is edited. Open the drivers/Makefile and add the following line in the end of the file.

obj-$(CONFIG_HELLO_MOD) += helloworld/

At this stage we are almost done with the configuration for the module. But now we have to reconfirm that Modules loading and unloading are enabled in the kernel config file.

Step 5:

In order to check this, goto the kernel source parent directory (in our example case kernel/goldfish) and than navigate to the "arch" directory and than from there navigate to the architecture for which you are building android. As most of the Android based smartphones are ARM based so most of the times you have to go into the arm directory unless you are building Android for some other architectures listed in the arch directory. As for our example we are using the android emulator which is emulating the ARM instruction set so, I have navigated to the "arm" directory and than from here to the "configs" directory.

Once you are in the “configs” directory you must have to know the config file which is being used by the Android/CyanogenMod build system to build your kernel. In our example case it is using the file "cyanogenmod_goldfish_armv7_defconfig". First backup this file to avoid mishaps. Than open this file with the editor of your choice and do the following changes in it.

a) Find the string CONFIG_MODULES. If it is found and shows like this :

# CONFIG_MODULES is not set

b) than, remove this line and add the following lines at the end of the file


CONFIG_MODULES=y
CONFIG_MODULE_FORCE_LOAD=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y

c) Note, If by searching CONFIG_MODULES does not find any thing than also add the above snippet at the end of the file.

Step 6:

Now at this stage we have do all the necessary setting in order to get our module working. Now it is time to build the android for your device/emulator. If you are using a real device you can either do an otapackage build or a normal build. As I am using emulator so I do not need the otapackage. I will go for the standard make build. Go to the Android source parent directory. If your shell session has been closed you have todo again the . build/envsetup.sh followed by the breakfast or lunch commands, otherwise you can simply write make in terminal from you Android/CyanogenMod source root. So, for me:

$ make -j4 
Or
$ make -j4 otapackage (if you are using a real device and want everything zipped so it can be used by the recovery to flash the zip on your device)

It would take tens of minutes to do the build if everything is smooth and fine. Once the build finishes now you can flash the zip on your device. As we are using emulator for our example, we need to do one step more because the emulator, normally uses a prebuilt kernel which most of the time is compiled with the CONFIG_MODULES=n. So the prebuilt kernel cannot load our modules and we have to change the prebuilt kernel image with the one which we have just compiled.
Step 7: This step only implies if you are using emulator. To replace the prebulit emulator kernel first backup the prebuilt emulator. The prebuilt emulator kernel file is "prebuilts/qemu-kernel/arm/kernel-qemu-armv7". Backup this  "kernel-qemu-armv7" file and than replace the "prebuilts/qemu-kernel/arm/kernel-qemu-armv7" image with the newly compiled kernel  image which is found at copy the "out/target/product/generic/kernel" . Now, once kernel is replaced from the Android/CyanogenMod source parent directory write the following command.

$ emulator-arm 

This will open the android emulator. wait for it to load properly. For me it show like this:

Step 7:

Once the emulator is up and running, it is time to test our module. So, open another shell terminal and and do the following:

$ adb shell

this will open the device shell and will looks like:

Now the module we have built can be found in the device "system/lib/modules" directory it is clear from the screenshot also. Now, we have to insert that module using "insmod" and than have to check the kernel message buffer whether the message is printed or not. you can remove the module using "rmmod". Now from the device shell insert the module using the following command, the output of the module is also listed in the above screenshot:


# insmod /system/lib/modules/hello.ko
# dmesg | grep "Hello"
Hello Kernel World....
# rmmod hello.ko
# dmesg | grep "Good"
Goodbye Kernel World

I hope you will enjoy this tutorial. Please make comments if you did not understand something i will try to clear it.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s