Linux device drivers are a critical part of the Linux operating system, enabling communication between the hardware and the kernel. They act as a bridge, allowing the operating system to control and interact with hardware devices such as keyboards, printers, network cards, and storage devices. In this article, we will explore the very basic fundamentals of Linux device drivers, their types.
What is a Linux Device Driver?
A device driver is a piece of software that allows the operating system to interact with hardware devices. In Linux, device drivers are implemented as kernel modules, which can be dynamically loaded and unloaded into the kernel without rebooting the system. This modular approach makes Linux highly flexible and adaptable to different hardware configurations.
Types of Linux Device Drivers
Linux categorizes devices into three main types, each requiring a specific type of driver:
1. Character Device Drivers
Character devices are accessed as a stream of bytes, similar to files. They are used for devices that require sequential data access, such as keyboards, mice, and serial ports.
Example: /dev/ttyS0 (serial port).
2. Block Device Drivers
Block devices are accessed in fixed-size blocks of data. They are typically used for storage devices like hard drives and SSDs.
Example: /dev/sda (hard disk).
3. Network Device Drivers
Network devices are used for communication over a network. They handle packet transmission and reception.
Example: eth0 (Ethernet interface).
Kernel Modules
Kernel modules are pieces of code that can be dynamically loaded into and unloaded from the Linux kernel at runtime. They extend the functionality of the kernel without requiring a reboot. Device drivers are a common type of kernel module.
Key Features of Kernel Modules
Dynamic Loading: Modules can be loaded using (insmod) and unloaded using (rmmod) without restarting the system.
Kernel Space Execution: Modules run in kernel space, giving them direct access to hardware and kernel data structures.
Dependencies: Modules can depend on other modules. The “modprobe” command automatically handles dependencies.
Commands for Managing Kernel Modules
insmod: Insert a module into the kernel. rmmod: Remove a module from the kernel. modprobe: Load a module and its dependencies. lsmod: List all loaded modules.
Example of a Simple Kernel Module

What are File Operations?
File operations are a set of functions defined in the file operations structure. These functions allow user-space applications to interact with device drivers using standard file I/O system calls like open, read, write, and ioctl.
Key Functions in file operations
open: Called when a device file is opened. release: Called when the device file is closed. read: Called when data is read from the device. write: Called when data is written to the device. ioctl: Used for device-specific commands.

Device Files
What are Device Files?
Device files are special files in the /dev directory that represent hardware devices. They allow user-space applications to interact with hardware through standard file I/O operations.
Types of Device Files
Character Device Files: Used for character devices (e.g., /dev/ttyS0).
Block Device Files: Used for block devices (e.g., /dev/sda).
Creating Device Files
Device files are created using the mknod command:
sudo mknod /dev/hello c 240 0
- c: Indicates a character device.
- 240: Major number (identifies the driver).
- 0: Minor number (identifies the device instance).
Automatic Device File Creation
Modern Linux systems use udev to automatically create device files when a module is loaded. You can use the class_create and device_create functions in your driver to achieve this.
Kernel APIs
Memory Management
kmalloc: Allocates contiguous memory in kernel space.
void *kmalloc(size_t size, gfp_t flags);
vmalloc: Allocates non-contiguous memory in kernel space.
void *vmalloc(unsigned long size);
kfree: Frees memory allocated by kmalloc.
void kfree(const void *ptr);
Synchronization
Mutexes: Used for mutual exclusion.
DEFINE_MUTEX(my_mutex); mutex_lock(&my_mutex); mutex_unlock(&my_mutex);
Spinlocks: Used for short-term locking in interrupt contexts.
spinlock_t my_lock; spin_lock(&my_lock); spin_unlock(&my_lock);
Hardware Access
I/O Ports: Used for accessing hardware registers.
unsigned char inb(unsigned short port); void outb(unsigned char value, unsigned short port);
Memory-Mapped I/O: Maps hardware registers to memory addresses.
void *ioremap(unsigned long phys_addr, unsigned long size); void iounmap(void *addr);
And that’s a wrap! Hopefully, this helped you understand the basics, and soon, you will be creating your own device drivers.
The only way to get better is to start doing it so let’s do it!