Step‑by‑Step Guide to Building Linux Kernel Modules and a Custom ARM Kernel
This tutorial walks you through the Linux kernel source layout, required build tools, cross‑compilation setup, how to compile a kernel image, generate device‑tree blobs, build standalone .ko modules, and integrate those modules into a custom kernel for ARM boards.
1. Kernel source tree
The Linux kernel source is organized as a large directory tree managed by Makefiles. The top‑level README provides an overview and basic build commands, while the top‑level Makefile coordinates configuration and compilation of all subdirectories.
arch/ – architecture‑specific code (e.g., arch/arm, arch/x86).
crypto/ – common encryption, hash, compression and CRC algorithms.
drivers/ – device drivers (character, block, etc.).
documentation/ – kernel documentation.
fs/ – file‑system implementations.
include/ – kernel header files ( include/asm‑*, include/linux).
init/ – kernel initialization code.
ipc/ – inter‑process communication.
kernel/ – core kernel code (relatively small).
lib/ – helper libraries such as zlib and crc32.
mm/ – memory‑management subsystem.
net/ – networking protocols.
sound/ – sound driver support.
scripts/ – various helper scripts.
usr/ – user‑space utilities.
2. Build tools
make mrproper– clean generated configuration and object files; run before the first build.
Import a default configuration, e.g. make xxx_deconfig or copy an existing .config from arch/arm/configs.
Configuration commands: make xxxxconfig – edit the configuration. make xconfig – Qt‑based graphical UI. make menuconfig – ncurses UI (install libncurses5-dev if needed). make config – minimal configuration. make uImage – generate the ARM kernel image ( arch/arm/boot/uImage). make dtbs – generate device‑tree blobs ( arch/arm/boot/dtb/*.dtb). make modules – compile all source files marked with M into loadable .ko modules.
3. Kernel compilation
After installing a cross‑compilation toolchain (e.g., arm-none-linux-gnueabi-gcc), download the desired kernel source, for example Linux 3.14, and extract it:
$ tar xvf linux-3.14.tar.xz
$ cd linux-3.14Edit the top‑level Makefile to set the cross‑compiler:
ARCH ?= arm
CROSS_COMPILE ?= arm-none-linux-gnueabi-Generate a default configuration for the target board (e.g., Exynos):
$ make exynos_defconfigThe resulting .config contains the kernel options required for the Samsung Exynos board.
Run make menuconfig to adjust options. Most options can be left at their defaults; select * for built‑in code, M for loadable modules, and leave unused options empty.
Finally build the kernel image: # make uImage The uImage appears in arch/arm/boot/.
4. Building a standalone driver module
Assume a simple character driver source file hello.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
static int major = 237;
static int minor = 0;
static dev_t devno;
static struct class *cls;
static struct device *class_dev;
static int hello_open(struct inode *inode, struct file *filep)
{ printk("hello_open()
"); return 0; }
static int hello_release(struct inode *inode, struct file *filep)
{ printk("hello_release()
"); return 0; }
#define KMAX_LEN 32
char kbuf[KMAX_LEN+1] = "kernel";
static ssize_t hello_read(struct file *filep, char __user *buf, size_t size, loff_t *pos)
{ if (size > strlen(kbuf)) size = strlen(kbuf);
if (copy_to_user(buf, kbuf, size)) return -EFAULT;
return size; }
static ssize_t hello_write(struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{ if (size > KMAX_LEN) size = KMAX_LEN;
memset(kbuf, 0, sizeof(kbuf));
if (copy_from_user(kbuf, buf, size)) return -EFAULT;
printk("%s
", kbuf);
return size; }
static struct file_operations hello_ops = {
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
};
static int __init hello_init(void)
{ int result;
printk("hello_init
");
result = register_chrdev(major, "hello", &hello_ops);
if (result < 0) { printk("register_chrdev fail
"); return result; }
cls = class_create(THIS_MODULE, "hellocls");
if (IS_ERR(cls)) { result = PTR_ERR(cls); goto out_err_1; }
devno = MKDEV(major, minor);
class_dev = device_create(cls, NULL, devno, NULL, "hellodev");
if (IS_ERR(class_dev)) { result = PTR_ERR(class_dev); goto out_err_2; }
return 0;
out_err_2:
class_destroy(cls);
out_err_1:
unregister_chrdev(major, "hello");
return result; }
static void __exit hello_exit(void)
{ printk("hello_exit
");
device_destroy(cls, devno);
class_destroy(cls);
unregister_chrdev(major, "hello"); }
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");Create a Makefile for the module:
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /home/peng/linux-3.14
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order
endifRun make in the module directory; the resulting hello.ko can be loaded on the target board with insmod if the kernel version matches.
5. Integrating the module into the kernel
Copy hello.c into an appropriate kernel subdirectory, e.g., drivers/char/. Edit the subdirectory Makefile to add the line:
obj-$(CONFIG_HELLO) += hello.oThen edit the corresponding Kconfig file to declare the option:
config HELLO
tristate "Hello character driver"
depends on ARCH_EXYNOS4
help
Simple hello world character driver.Run make menuconfig again, locate the new entry under Device Drivers → Character devices , and set it to * (built‑in) or M (module). Save the configuration, then rebuild the kernel with make uImage. The driver will be compiled into the new kernel image or produced as a loadable .ko file.
6. Additional notes
The final .config must contain CONFIG_HELLO=y (or M) for the driver to be included. Alternatively, you can manually add the macro to .config without editing Kconfig. After rebuilding, the kernel image contains the driver, and the board can boot with the new functionality.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
