How Linux’s Device Driver Model and sysfs Work Together: A Deep Dive
This article explains the Linux kernel device driver model introduced in 2.6, how devices, buses, classes and drivers are represented, how the sysfs virtual filesystem exposes their hierarchy, and walks through the registration and matching process for platform buses, drivers, and devices with detailed code examples.
Linux Device Driver Model and sysfs
The Linux kernel added a unified device driver model in version 2.6 to simplify driver development. The model consists of device , bus , class and driver objects that are linked together; devices and drivers are bound via a bus.
In the kernel, the structures bus_type, device_driver and device (defined in linux/device.h) describe buses, drivers and devices. Both device_driver and device contain a pointer to the associated struct bus_type.
sysfs is a virtual filesystem that presents the device‑driver hierarchy as a directory tree accessible from user space. A typical top‑level /sys layout looks like:
/sys$ ll
total 0
drwxr-xr-x 2 root root 0 Aug 20 15:27 block/
drwxr-xr-x 29 root root 0 Aug 20 15:27 bus/
drwxr-xr-x 61 root root 0 Aug 20 15:27 class/
drwxr-xr-x 4 root root 0 Aug 20 15:27 dev/
drwxr-xr-x 14 root root 0 Aug 20 15:27 devices/
drwxr-xr-x 4 root root 0 Aug 20 15:27 firmware/
drwxr-xr-x 8 root root 0 Aug 20 15:27 fs/
drwxr-xr-x 2 root root 0 Sep 2 17:08 hypervisor/
drwxr-xr-x 8 root root 0 Aug 20 15:27 kernel/
drwxr-xr-x 147 root root 0 Aug 20 15:27 module/
drwxr-xr-x 2 root root 0 Aug 20 15:27 power/Important sub‑directories: block: contains block devices such as ram, sda etc. bus: holds all bus types (e.g., pci, usb, i2c). class: groups devices by type (e.g., input, pci_bus, mmc_host). dev: contains char and block sub‑directories for major/minor numbers, linking to /sys/devices. devices: lists every device present in the system.
Each object shown in sysfs corresponds to a kobject (defined in linux/kobject.h) and is grouped in a kset. The kernel connects kobject, kset and parent pointers to build a hierarchical representation that matches the driver model.
Platform Bus Registration
The platform bus is a virtual bus used for devices that cannot attach to a physical bus (e.g., on‑SoC peripherals). Its registration starts in start_kernel() via platform_bus_init():
start_kernel() → arch_call_rest_init() → rest_init() → kernel_init() → kernel_init_freeable() → do_basic_setup() → driver_init() → platform_bus_init()The kernel defines struct bus_type platform_bus_type and a struct device platform_bus to manage devices on this bus:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops,
};
struct device platform_bus = {
.init_name = "platform",
}; platform_bus_init()performs two steps:
device_register(&platform_bus) bus_register(&platform_bus_type)device_register(&platform_bus)
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}This initializes the kobject, device_private, mutexes, etc., and adds the platform bus as a device under /sys/devices/platform.
bus_register(&platform_bus_type)
int bus_register(struct bus_type *bus)
{
struct subsys_private *priv;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
priv->bus = bus;
bus->p = priv;
/* create kset for the bus */
retval = kset_register(&priv->subsys);
/* create "devices" and "drivers" sub‑folders */
priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);
/* other initialisation omitted for brevity */
return 0;
}The call creates the /sys/bus/platform directory with devices and drivers sub‑folders.
Platform Driver Registration
A platform driver is described by struct platform_driver, which embeds a struct device_driver:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};Example driver globalfifo_driver:
static struct platform_driver globalfifo_driver = {
.driver = {
.name = "globalfifo_platform",
.owner = THIS_MODULE,
},
.probe = globalfifo_probe,
.remove = globalfifo_remove,
};Registration is performed by platform_driver_register(&globalfifo_driver), which calls __platform_driver_register() and ultimately driver_register():
int __platform_driver_register(struct platform_driver *drv, struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
} driver_register()validates the bus, checks for existing drivers with the same name, adds the driver to the bus’s klist_drivers, creates sysfs entries, and finally triggers a KOBJ_ADD uevent.
bus_add_driver(&globalfifo_driver.driver)
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus = bus_get(drv->bus);
struct driver_private *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe)
driver_attach(drv);
module_add_driver(drv->owner, drv);
driver_create_file(drv, &driver_attr_uevent);
driver_add_groups(drv, bus->drv_groups);
return 0;
}The driver is now visible under /sys/bus/platform/drivers/globalfifo_platform and can be matched to devices.
Platform Device Registration
A platform device is described by struct platform_device which embeds a struct device:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override;
/* arch‑specific fields omitted */
};Example device globalfifo_device:
static struct platform_device globalfifo_device = {
.name = "globalfifo_platform",
.id = -1,
};Registration entry point:
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}platform_device_add(&globalfifo_device)
int platform_device_add(struct platform_device *pdev)
{
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
/* set device name based on id */
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
/* resource handling omitted */
return device_add(&pdev->dev);
} device_add()creates the kobject for the device, links it under /sys/devices/platform, registers it with the bus, creates sysfs files, and finally calls bus_probe_device() to look for a matching driver.
bus_probe_device(&globalfifo_device.dev)
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
if (bus && bus->p->drivers_autoprobe)
device_initial_probe(dev);
}
void device_initial_probe(struct device *dev)
{
__device_attach(dev, true);
}
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
struct device_attach_data *data = _data;
struct device *dev = data->dev;
int ret = driver_match_device(drv, dev);
if (ret > 0)
return driver_probe_device(drv, dev);
return 0;
}The matching process eventually calls platform_match(), which tries driver‑override, OF (device‑tree), ACPI, ID table, and finally name matching.
Summary of Registration Flow
Bus registration: bus_register() Driver registration:
platform_driver_register() → driver_register() → bus_add_driver()Device registration:
platform_device_add() → device_add() → bus_add_device() / bus_probe_device()Through sysfs, the relationships are visible as:
/sys/bus/platform/drivers/globalfifo_platform
bind
globalfifo_platform -> ../../../../devices/platform/globalfifo_platform/
module -> ../../../../module/globalfifo_platform/
uevent
unbind
/sys/bus/platform/devices
globalfifo_platform -> ../../../devices/platform/globalfifo_platform/
/sys/devices/platform/globalfifo_platform
driver -> ../../../bus/platform/drivers/globalfifo_platform/
modalias
power/
subsystem -> ../../../bus/platform/
ueventThese structures illustrate how the kernel links platform devices, drivers, and the bus, and how sysfs exposes the hierarchy for user‑space tools.
References
Song Baohua, Linux Device Driver Development Detailed (Linux 4.0) , 2016.
Knowledge整理 – Linux Device Driver Model: https://blog.csdn.net/TongxinV/article/details/54853122
Linux Device Driver Model: https://blog.csdn.net/qq_40732350/article/details/82992904
Original article: "Linux设备驱动模型简述(源码剖析)"
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
