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":
static int myinit(void)
printk(KERN_INFO "hello init\n");
static void myexit(void)
printk(KERN_INFO "hello exit\n");
After build, you can use it with:
and it prints
hello init to
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/hello.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