A kernel module may not be a device driver at all
"Kernel driver" is not a well defined term, but let's give it a shot.
This is a kernel module that does not drive any hardware, and thus could not be reasonably considered a "device driver":
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
static int myinit(void)
{
printk(KERN_INFO "hello init\n");
return 0;
}
static void myexit(void)
{
printk(KERN_INFO "hello exit\n");
}
module_init(myinit)
module_exit(myexit)
After build, you can use it with:
insmod hello.ko
and it prints hello init
to dmesg
.
There are, however, kernel modules that are not device drivers, but are actually useful, e.g., modules that expose kernel debugging / performance information.
Device drivers are usually also kernel modules.
An example of something that is a "device driver" is a bit harder to generate, since it requires a hardware to drive, and hardware descriptions tend to be complicated.
Using QEMU or other emulators however, we can construct software models of real or simplified hardware, which is a great way to learn how to talk to hardware.
Here is a simple example of a minimal PCI device driver: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/pci.c
We then see that in x86, talking to hardware comes down to:
Those operations cannot in general be done from userland, as explained at: What is difference between User space and Kernel space? There are however some exceptions: https://stackoverflow.com/questions/7986260/linux-interrupt-handling-in-user-space.
The kernel then offers higher level APIs to make such hardware interaction easier and more portable:
request_irq
to handle interrupts
ioreadX
and IO memory mapping
- even higher level interfaces for popular protocols like PCI and USB