If you have already followed “Writing first Linux kernel Module and understanding Kernel module compilation, loading and unloading”, you might have already understood how to build simple kernel module for x86 which we compiled from outside kernel source code for a x86 system which was already running with Linux kernel,
Now, Lets try to integrate same kernel module as part of Linux kernel source code, so we can also cross compile the same for ARM and also set an option to make it as part of kernel binary ( zImage/uImage) or build it as module using kernel build framework.
Follow, steps from “Cross Compilation and Booting of Linux kernel for Raspberry Pi3 – Manual Compilation” to clone kernel source code for Raspberry Pi, toolchain and setup the environment to cross compile kernel image zImage and modules from the default configuration.
$ cd linux
create a directory to write a kernel module as,
$ mkdir drivers/helloworld/
Now, lets write our basic helloworld kernel module / device driver in drivers/helloworld/helloworld.c
$ vim drivers/helloworld/helloworld.c
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
static int __init hello_init(void) {
printk(KERN_INFO "Hello, world\n");
return 0;
}
static void __exit hello_exit(void) {
printk(KERN_INFO "Goodbye, world\n");
}
module_init(hello_init);
module_exit(hello_exit);
To, get this driver module compiled as part of kernel, we need to write Makefile and Kconfig file by following Linux kernel build mechanism, the contents of those files will be as below,
$ vim drivers/helloworld/Makefile
obj-$(CONFIG_HELLOWORLD) += helloworld.o
$ vim drivers/helloworld/Kconfig
#
# Helloworld driver as part of kernel source
#
menu "Helloworld Driver"
config HELLOWORLD
depends on ARM
tristate "helloworld module"
default y
help
Helloworld kernel module integrated as part of kernel source.
endmenu
here, as part of Kconfig, we are telling the Kernel build framework, that this driver can have 3 states for configuration as, “not selected”, “y” i.e. selected as part of kernel image and “m” selected to build as kernel module which is indicated by “tristate” , configuration parameter “depends” tells build framework to select this kernel module if “ARM” has been selected, ( you can change as you want to set dependency ), parameter “default y” selects this Confifuration parameter HELLOWORLD as “CONFIG_HELLOWORLD=y” in the default configuration, where as “help” shows the respective information in “make menuconfig”
Now, we need to tell the kernel build framework to consider our newly created driver directory for the compilation, by appending following line into “drivers/Kconfig” as,
$ vim drivers/Kconfig
source "drivers/helloworld/Kconfig"
and
append following line to drivers/Makefile as,
$ vim drivers/Makefile
obj-$(CONFIG_HELLOWORLD) += helloworld/
Now, we are ready to start the compilation of the kernel, currently because we have mentioned “config HELLOWORLD” as “default y” , if we do
$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- menuconfig
In above command, you need to use toolchain “arm-bcm2708-linux-gnueabi” as per your build system.
we will see following things in “Device Drivers —> Helloworld Driver —> <*> helloworld module” in “Help”
You can use space bar to select and deselect “helloworld module” for,
1. deselect as < > helloworld module
2. select as module, helloworld module
3. select as in built to kernel image as, <*> helloworld module
after selecting proper type as above, click “Exit” till you see following window,
Do you wish to save your new configuration?
│ (Press to continue kernel configuration.)
│ ├──────────────────────────────────────────────────────────┤
| < Yes > < No >
select “yes” in this as if you open updated, .config file you will see a new option added as,
#
# Helloworld Driver
#
CONFIG_HELLOWORLD=y
Now, lets build kernel image, as
$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- zImage
CHK include/config/kernel.release
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
CHK include/generated/bounds.h
CHK include/generated/timeconst.h
CHK include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
CC drivers/helloworld/helloworld.o
LD drivers/helloworld/built-in.o
LD drivers/built-in.o
LD vmlinux.o
MODPOST vmlinux.o
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
LD init/built-in.o
KSYM .tmp_kallsyms1.o
KSYM .tmp_kallsyms2.o
LD vmlinux
SORTEX vmlinux
SYSMAP System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy_data
AS arch/arm/boot/compressed/piggy.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
Now to compile same driver as kernel module, select it as “m” in kernel menuconfig as,
$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- menuconfig
we will see following things in “Device Drivers —> Helloworld Driver —> helloworld module”
and then compile as,
$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- modules
scripts/kconfig/conf --silentoldconfig Kconfig
CHK include/config/kernel.release
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
CC kernel/bounds.s
CHK include/generated/bounds.h
CHK include/generated/timeconst.h
CC arch/arm/kernel/asm-offsets.s
CHK include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
CC scripts/mod/empty.o
MKELF scripts/mod/elfconfig.h
HOSTCC scripts/mod/modpost.o
CC scripts/mod/devicetable-offsets.s
GEN scripts/mod/devicetable-offsets.h
HOSTCC scripts/mod/file2alias.o
HOSTCC scripts/mod/sumversion.o
HOSTLD scripts/mod/modpost
GZIP kernel/config_data.gz
CHK kernel/config_data.h
UPD kernel/config_data.h
CC [M] kernel/configs.o
CC [M] drivers/helloworld/helloworld.o
Building modules, stage 2.
MODPOST 1561 modules
CC drivers/helloworld/helloworld.mod.o
LD [M] drivers/helloworld/helloworld.ko
CC kernel/configs.mod.o
LD [M] kernel/configs.ko
Now, we have to install this modules for copying into hardware, to do that we have to create separate directory for output binaries and setting INSTALL_MOD_PATH to this directory,
$ mkdir out
$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- modules_install INSTALL_MOD_PATH=./out
$ make ARCH=arm CROSS_COMPILE=arm-bcm2708-linux-gnueabi- modules_install INSTALL_MOD_PATH=./out