A progression of small, self-contained Linux kernel modules that build up from a bare "hello world" in kernel space to a working character device driver with dynamic major/minor allocation.
Each module isolates a single kernel or driver concept so it is clear what changed from one to the next. Together they demonstrate:
- The loadable kernel module lifecycle:
module_init/module_exit,insmod/rmmod, and kernel logging throughprintk(viewed withdmesg). - Passing parameters into a module at load time with
module_param. - Registering device numbers, both with a statically chosen major (
register_chrdev) and with dynamic allocation (alloc_chrdev_region). - The
file_operationsinterface wiringopen/read/write/releaseon a/devnode through to driver callbacks. - Moving data across the user/kernel boundary with
copy_to_userandcopy_from_user. - Managing kernel memory with
kmalloc,krealloc, andkfree, including a device buffer that grows on demand.
A separate board-pynq-z1/ wing documents taking the character driver off the host x86_64 kernel and onto ARM hardware by building a Yocto image and cross-compiling for the target (see below).
- Linux kernel headers matching the running kernel:
linux-headers-$(uname -r). - A C toolchain and build tools:
gcc,make(for example, Debian/Ubuntubuild-essential). - Root privileges to load and unload modules and to create device nodes.
Every module builds against the running kernel via the standard kbuild pattern:
make -C /lib/modules/$(uname -r)/build M=$(PWD) modules.
The board-pynq-z1/ wing is different: it targets a Pynq-Z1 board (Xilinx Zynq-7000, dual Cortex-A9, armv7 hard-float) running a Yocto Scarthgap (5.0 LTS) image built from source. That path needs the Yocto build dependencies and a cross-toolchain, all documented inside that directory. It is a documentation and configuration wing (build notes plus conf/ snapshots); the heavy Yocto tree lives outside the repo.
hello_kernel/(mymodule.c): a minimal loadable module with__init/__exitentry points that log a message viaprintk. The smallest complete module.hello_mod/(hello_world.c): a second minimal module usingKERN_INFOlog levels and theMODULE_LICENSE/MODULE_AUTHOR/MODULE_DESCRIPTIONmetadata macros.mod_parameter/(module_parameters.c): declares module parameters (howManyasint,whomascharp) withmodule_param, then prints the greetinghowManytimes at load. Shows how values are supplied atinsmodtime.dev_num/(dev_num.c): registers a character device number with a statically defined major (91) viaregister_chrdev, and providesopen/releasecallbacks through afile_operationstable.test.cis a user-space program that opens the device node and reports success or failure.chardev/(chardev.c): a full character device driver. It allocates a major/minor pair dynamically withalloc_chrdev_region, registers acdev, and implementsopen,release,read, andwrite. Reads and writes cross the user/kernel boundary withcopy_to_user/copy_from_user, and the backing buffer starts at 1024 bytes and grows on demand withkrealloc.test_app.cis an interactive menu-driven program that opens/dev/mychardevand writes to and reads from it.board-pynq-z1/: documentation and configuration for the cross-compile wing. ContainsREADME.md(plan and phase tracking),TUTORIAL.md(a reproducible, start-to-finish Yocto Scarthgap build log for the Pynq-Z1), andconf/(bblayers.confand alocal.confsnippet). The build tree itself is kept outside the repo.
Build any module by running make in its directory:
cd chardev # or hello_kernel, hello_mod, mod_parameter, dev_num
make # builds the .ko against the running kernelLoad it, inspect the kernel log, then unload it:
sudo insmod chardev.ko
dmesg | tail # view init messages and the allocated major/minor
sudo rmmod chardevPass parameters at load time (mod_parameter):
sudo insmod module_parameters.ko howMany=3 whom=World
dmesg | tail
sudo rmmod module_parametersTesting the character device (chardev): the module allocates its major number dynamically and does not create the /dev node automatically, so read the major from the log and create the node by hand:
sudo insmod chardev.ko
dmesg | tail # note the "major X and minor 0" line
sudo mknod /dev/mychardev c <major> 0 # use the major from dmesg
sudo chmod 666 /dev/mychardev
echo "hello" > /dev/mychardev # write
cat /dev/mychardev # read back
# or use the interactive helper (built by the chardev Makefile)
./test_app
sudo rm /dev/mychardev
sudo rmmod chardevThe dev_num module works the same way (static major 91): create the node with sudo mknod /dev/<name> c 91 0, then open it (its test.c opens /dev/mydevices).
A note on safety: build against a kernel-headers version that matches the running kernel, and load modules on a VM or test machine rather than anything you care about.
hello_kernel/— minimal module withinit/exitandprintk.hello_mod/— minimal module with metadata macros and log levels.mod_parameter/— module parameters supplied at load time.dev_num/— static device-number registration withopen/releasecallbacks.chardev/— character device driver: dynamic major/minor,read/write, growable buffer, plus a user-space test app.board-pynq-z1/— Yocto + ARM cross-compile wing (docs and build config).