« Previous Lecture | Next Lecture »
In this free Linux device drivers course lecture, you will learn to test a Linux character driver from user space using nothing more than the standard dd command. Once a driver is loaded into the kernel, the only real way to prove it works is to open, read, write, and close its device node exactly as any application would — and that is exactly what we practice here.
Why Testing a Linux Character Driver Matters
Writing a Linux character driver is only half the job. Before you trust a driver, you need to prove that user space can genuinely reach it through the standard file API — open(), read(), write(), and close(). This free Linux device drivers course lecture focuses on the fastest way to do that: the humble dd utility, already present on every Linux system.
Unlike writing a dedicated C test application, dd lets you exercise a driver’s file operations in seconds, straight from the shell, while watching the kernel log confirm exactly what happened inside your driver.
Prerequisites
Before starting this lecture, make sure you are comfortable with the following, covered in earlier lectures of this free Linux kernel development course:
- Writing and loading a basic character driver as a kernel module
- The
file_operationsstructure and how the VFS dispatches to it - Using
insmod,rmmod, anddmesg
A Minimal Driver We Will Test
To keep this lecture focused purely on testing methodology, here is a compact character driver whose read and write methods simply log what user space asked for. Load this driver (or your own) before continuing.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#define DEV_NAME "eptest_chardrv"
static ssize_t eptest_read(struct file *fp, char __user *ubuf,
size_t count, loff_t *off)
{
pr_info("%s: user requested %zu bytes to read\n", DEV_NAME, count);
return count;
}
static ssize_t eptest_write(struct file *fp, const char __user *ubuf,
size_t count, loff_t *off)
{
pr_info("%s: user sent %zu bytes to write\n", DEV_NAME, count);
return count;
}
static int eptest_open(struct inode *inode, struct file *fp)
{
pr_info("%s: device opened by PID %d\n", DEV_NAME, current->pid);
return 0;
}
static int eptest_release(struct inode *inode, struct file *fp)
{
pr_info("%s: device closed\n", DEV_NAME);
return 0;
}
static const struct file_operations eptest_fops = {
.owner = THIS_MODULE,
.open = eptest_open,
.read = eptest_read,
.write = eptest_write,
.release = eptest_release,
};
static struct miscdevice eptest_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &eptest_fops,
};
static int __init eptest_init(void)
{
int ret = misc_register(&eptest_miscdev);
if (ret) {
pr_err("%s: misc_register failed: %d\n", DEV_NAME, ret);
return ret;
}
pr_info("%s: registered, /dev/%s is ready\n", DEV_NAME, DEV_NAME);
return 0;
}
static void __exit eptest_exit(void)
{
misc_deregister(&eptest_miscdev);
pr_info("%s: unregistered\n", DEV_NAME);
}
module_init(eptest_init);
module_exit(eptest_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala test driver for dd-based testing lecture");
Build and load it the usual way:
make
sudo insmod eptest_chardrv.ko
ls -l /dev/eptest_chardrv
Understanding What dd Actually Does
dd is a low-level data-copying utility. It reads from an input source given by if= and writes to an output destination given by of=, moving data in fixed-size blocks controlled by bs=, repeated count= times. Because a device node behaves like a regular file to dd, pointing if= or of= at your device node forces dd to call open(), then read() or write(), and finally close() on it — precisely the sequence a real application would trigger.
Testing the Read Path
To exercise the driver’s read method, point if= at the device node and write the output to a plain file:
sudo dmesg -C
sudo dd if=/dev/eptest_chardrv of=readtest.bin bs=4k count=1
dmesg
You should see kernel log lines confirming the open call, the read request for 4096 bytes, and the close call, in that exact order. That ordering is your proof that user space really did travel through the driver’s file_operations table.
Testing the Write Path
Now exercise the write method by feeding random bytes from /dev/urandom into the device node:
sudo dmesg -C
sudo dd if=/dev/urandom of=/dev/eptest_chardrv bs=4k count=1
dmesg
This time the kernel log should show the open call, a write request for 4096 bytes, and the close call. Because our sample driver’s write method only logs and does not actually store anything, no persistent state changes — but the call path itself is fully validated.
Verifying the Data with hexdump
Since our test driver’s read method never actually fills the caller’s buffer, the safest way to confirm exactly what bytes user space received is to inspect the output file with hexdump:
hexdump -C readtest.bin | head
An uninitialized read from a driver that never touches the user buffer will typically show whatever dd pre-filled its buffer with (commonly zeros), which is a useful reminder that returning “success” from a read method is meaningless unless you actually copy real data into the buffer — a topic covered fully in the next lecture on copy_to_user() and copy_from_user().
Comparing dd Against a Custom Test Application
| Approach | Setup Effort | Best Used For |
|---|---|---|
| dd command | None — always available | Quick smoke tests of open/read/write/close |
| Custom C test app | Requires writing and compiling code | Testing ioctl(), specific buffer sizes, error paths |
| Python/bash scripts | Low, but slower than native calls | Automated regression testing |
Common Mistakes When Testing with dd
- Forgetting sudo: most device nodes are root-owned by default, so plain
ddcalls silently fail with a permission error. - Not clearing dmesg first: running
sudo dmesg -Cbefore each test keeps the log focused on the current test run only. - Assuming a returned byte count means real data was transferred: a driver can return “success” without ever touching the buffer, as our example shows.
- Using a huge count value on a driver with no natural EOF: this can make
ddappear to hang; always start withcount=1.
Best Practices
- Always clear the kernel log with
dmesg -Cbefore each individual test so output stays easy to read. - Test read and write paths separately, not in a single command, so you can correlate log lines cleanly.
- Follow up any read test with
hexdumpto check actual byte content, not just the reported count. - Once
ddtesting passes, move on to a small C test application to cover edge cases like partial reads and error codes.
Security Considerations
Device nodes created by drivers are, by default, only accessible to root unless you add udev rules to relax permissions. Keep this restrictive default during development and testing — loosening permissions on a device node that performs privileged operations is a common source of local privilege-escalation bugs in real-world driver code.
Key Takeaways
ddis the fastest way to trigger a driver’sopen(),read(),write(), andclose()methods without writing any test code.bs=andcount=control exactly how much data moves through the driver in a single test run.- The kernel log is your source of truth — always cross-check it against what you expect the driver to do.
- A byte count returned by
read()does not guarantee real data was copied; verify withhexdump.
Conclusion
Testing is not an optional afterthought in driver development — it is how you build confidence that your character driver behaves correctly under the exact system calls real applications will issue. The dd command gives you a zero-setup way to validate the open, read, write, and close paths of any character driver in seconds, and reading the kernel log alongside it turns a black-box test into a fully observable one. In the next lecture of this free Linux device drivers course, we go one level deeper and actually move real data between kernel and user space using copy_to_user() and copy_from_user().
Frequently Asked Questions
Why use dd instead of cat to test a character driver?
dd gives explicit control over block size and count via bs= and count=, which is important for drivers that expect data in specific chunk sizes. cat has no such controls.
Do I need root permissions to test a character driver with dd?
Usually yes, since device nodes are root-owned by default unless a udev rule changes ownership or permissions.
Why did my dd command hang?
This typically happens when count= is omitted on a driver that never signals EOF, so dd keeps requesting more data indefinitely. Always specify count= explicitly during testing.
Can dd test ioctl() calls on my driver?
No, dd only exercises open, read, write, and close. Testing ioctl() requires a small custom C or Python test program.
How do I know my driver’s read method actually copied data, not just returned a count?
Inspect the output file with hexdump. If the driver never wrote real data into the user buffer, you will typically see the buffer’s original contents, often zeros.
Is dd testing enough before shipping a driver?
No. dd testing is a good first smoke test, but production drivers need dedicated test applications covering error paths, concurrent access, and edge-case buffer sizes.
What does bs=4k mean in a dd command?
It sets the block size dd uses for each individual read or write system call, here 4096 bytes, matching a typical page size.
This lecture is part of EmbeddedPathashala’s free Linux kernel development and device drivers course.
Explore More Free Courses
2 Comments