How Linux Character Device Drivers Work: From Open Call to Kernel Integration
This guide explains what a Linux driver is, classifies character, block and network devices, walks through the open() system call flow from user space to the kernel’s VFS and driver list, and shows how to write, compile, and load a simple character device driver module.
What Is a Driver?
A driver encapsulates operations on low‑level hardware and provides function interfaces to upper layers. Linux classifies devices into three types: character devices , block devices , and network devices .
Device Types
Character devices : accessed byte‑by‑byte in a stream fashion (e.g., mouse, keyboard, serial ports). Typical driver functions are open, close, read, and write.
Block devices : allow reading fixed‑size blocks from any offset (e.g., hard disks, USB sticks, SD cards).
Network devices : can be hardware (e.g., NIC) or virtual (e.g., loopback) interfaces that send and receive packets.
Example Call Flow
In user space, the program calls open("/dev/pin4", O_RDWR). This triggers a software interrupt (INT 0x80) that transfers control to the kernel.
The kernel entry system_call looks up the device name, obtains the device number, and forwards the request to the VFS.
The VFS function sys_open searches the driver list using the major and minor numbers, finds the pin4 driver’s open function, and executes it (the driver manipulates registers to control the I/O pin).
User‑Space vs. Kernel‑Space
User space programs use the C library (glibc) to call functions such as open , read , write , fork , pthread , socket . These APIs ultimately invoke system calls that run in kernel space. Kernel space contains the device driver code that directly controls hardware. The driver registers a device file under /dev (or /sys ) and implements file operations.
Device Numbers and the Driver List
Each device file has a major number (identifies the driver class) and a minor number (identifies a specific device). The kernel maintains a driver list; registration adds the driver’s file_operations structure to this list, and lookup uses the device numbers.
System Call Dispatch
The system_call function reads the system‑call number from the eax register, multiplies it by 4 to get an offset, and indexes the sys_call_table to find the corresponding service routine (e.g., sys_open, sys_write).
Character Device Driver Framework
The minimal driver consists of:
#include <linux/module.h> // module_init, module_exit
#include <linux/fs.h> // file_operations
#include <linux/cdev> // cdev structure
#include <linux/uaccess.h> // copy_from_user
static int major = 231; // major number
static int minor = 0; // minor number
static char *dev_name = "pin4"; // device name
static int pin4_open(struct inode *inode, struct file *file)
{
printk("pin4_open
");
return 0;
}
static ssize_t pin4_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
printk("pin4_write
");
return 0;
}
static const struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
};
static int __init pin4_drv_init(void)
{
dev_t devno = MKDEV(major, minor);
int ret = register_chrdev(major, dev_name, &pin4_fops);
if (ret < 0) return ret;
class_create(THIS_MODULE, "myfirstdemo");
device_create(NULL, NULL, devno, NULL, dev_name);
return 0;
}
static void __exit pin4_drv_exit(void)
{
unregister_chrdev(major, dev_name);
// class_destroy and device_destroy omitted for brevity
}
module_init(pin4_drv_init);
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");Compiling the Module
Place the source file in the kernel source tree (e.g., /SYSTEM/linux-rpi-4.19.y/drivers/char) and add obj-m += pin4driver.o to the Makefile. Then run:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modulesThe build produces .o (object) and .ko (kernel module) files. The .ko file contains extra sections such as .modinfo used by modprobe.
Loading and Testing the Driver
Copy the .ko to the Raspberry Pi and load it with sudo insmod pin4driver.ko. The device appears as /dev/pin4.
Adjust permissions: sudo chmod 666 /dev/pin4.
Run a user‑space test program (e.g., pin4test.c) that opens the device and writes a byte.
Check kernel messages with dmesg | grep pin4 to see the driver’s printk output.
Unload the driver with sudo rmmod pin4driver.
Why Build the Module on a VM?
Compiling a kernel module requires the exact kernel source and toolchain for the target architecture. Building on the Raspberry Pi is possible but extremely slow; using a cross‑compilation environment on a VM speeds up the process.
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.
