The Linux kernel driver model is a unified representation of all hardware in the system. Before it existed, each bus subsystem (PCI, USB, ISA, and so on) maintained its own ad-hoc data structures and device lists. The driver model introduced three common abstractions—Documentation Index
Fetch the complete documentation index at: https://mintlify.com/DeelerDev/linux/llms.txt
Use this file to discover all available pages before exploring further.
struct device, struct device_driver, and struct bus_type—that every bus now builds on. This consistency makes power management, hotplug, and sysfs representation work uniformly regardless of the underlying bus.
The three core abstractions
struct device
struct device is the generic representation of one hardware instance. Bus-specific structures embed it:
struct device carries the kobject (for sysfs), a pointer to the parent device, a pointer to the bus type, and PM state. Bus and driver code accesses the embedded device with container_of():
include/linux/device.h:
parent, name, and bus before calling device_register().
struct device_driver
struct device_driver represents the driver as a whole, not one device instance. It is statically allocated:
struct device_driver and add bus-specific fields such as device ID tables:
struct bus_type
struct bus_type represents one bus technology. It is declared as a static object by the bus driver:
match callback is the bus’s way of deciding whether a given driver supports a given device. The kernel calls it during driver registration and during device discovery.
Driver registration
pci_register_driver(), platform_driver_register(), etc.) that ultimately call driver_register() internally. Registration initializes the reference count, the lock, and the driver’s sysfs directory. Drivers should register early—these fields are accessed by the core at any time.
Bus matching: how the kernel pairs devices and drivers
When a new driver is registered, the bus walks its list of unbound devices and callsmatch() for each one. When a new device is discovered, the bus walks its list of registered drivers and calls match(). The bus-specific match() implementation compares the device’s identity (PCI vendor/device ID, USB product ID, Device Tree compatible string, etc.) against the driver’s ID table.
You iterate over all devices bound to a driver or all drivers on a bus using the helper functions
driver_for_each_dev(), bus_for_each_dev(), and bus_for_each_drv(). These helpers acquire the bus lock and maintain proper reference counts on each object.sysfs representation
Every registered bus, device, and driver gets a corresponding directory in sysfs. The layout reflects the physical hierarchy:/sys/bus/<bus>/devices/ are symlinks into the physical hierarchy under /sys/devices/. Driver directories under /sys/bus/<bus>/drivers/ contain a devices/ subdirectory that holds symlinks to bound devices, plus any attributes the driver exports.
Drivers export sysfs attributes using DRIVER_ATTR_RW and DRIVER_ATTR_RO macros, then add or remove them with:
Platform devices and platform_driver
Platform devices are SoC-integrated peripherals (UARTs, I2C controllers, GPIO banks) that appear as autonomous entities with direct CPU-bus addressing. They use a pseudo-bus called the platform bus.module_init() / module_exit() wrappers that call platform_driver_register() and platform_driver_unregister().
probe() and remove() callbacks
probe(): binding the driver to a device
probe(): binding the driver to a device
probe() is called in task context when the kernel decides a driver should own a device. A typical probe function:- Retrieves device resources (memory-mapped I/O regions, IRQs, clocks).
- Allocates and initializes driver-private data.
- Initializes the hardware.
- Registers the device with a higher-level subsystem (e.g.,
input_register_device()).
-EPROBE_DEFER if a dependency (clock, regulator, GPIO) is not yet available; the core will retry later.remove(): unbinding the driver
remove(): unbinding the driver
remove() is called when the device is physically removed, the driver module is unloaded, or the system is shutting down. Free all resources that were not allocated with devm_* helpers.sync_state(): synchronizing with boot firmware
sync_state(): synchronizing with boot firmware
sync_state() is called once, after all consumer devices of a given device have successfully probed. Its primary use case is letting the kernel cleanly take over management of devices configured by boot firmware (bootloaders, UEFI, ACPI) without disrupting consumers that haven’t finished initializing.Device resources: devm_* managed allocators
Thedevm_* family of functions ties resource lifetime to a struct device. All resources acquired through devm_* are automatically released when the device is unbound from its driver—whether remove() returns normally, probe() fails partway through, or the driver module is unloaded.
Common managed allocators:
| Function | Replaces |
|---|---|
devm_kzalloc(dev, size, gfp) | kzalloc() + manual kfree() |
devm_ioremap_resource(dev, res) | ioremap() + iounmap() |
devm_platform_ioremap_resource(pdev, idx) | platform_get_resource() + ioremap() |
devm_request_irq(dev, irq, handler, ...) | request_irq() + free_irq() |
devm_clk_get(dev, id) | clk_get() + clk_put() |
devm_regulator_get(dev, id) | regulator_get() + regulator_put() |
