On this page

Device Model

Coverage: kobject/kset → device/driver/bus/class → sysfs binding → probe lifecycle → udev → device tree (DT) → ACPI → platform device Kernel versions: 2.6 ~ 6.x

Overview

The Linux device model is an internal kernel framework that provides a unified representation and lifecycle management for all devices, drivers, and buses. Its core value lies not in "how devices work," but in "how devices and drivers find each other"—namely, device-driver binding.

kobject: The Basic Building Block

// include/linux/kobject.h
struct kobject {
    const char      *name;           // Name displayed in sysfs
    struct kobject  *parent;         // Parent node → sysfs directory hierarchy
    struct kset     *kset;           // Belonging kset (collection)
    struct kernfs_node *sd;         // dentry in sysfs (kernfs)
    struct kref     kref;            // Reference count
    unsigned int    state_initialized:1;
};

// kobject itself does not manage devices — it provides:
//   1. Directory/file representation in sysfs
//   2. Reference counting → automatic release
//   3. Parent-child hierarchical relationships

kset: A Collection of kobjects

struct kset {
    struct list_head list;           // List of all member kobjects
    struct kobject  kobj;            // Embedded kobject (the collection itself in sysfs)
    const struct kobj_type *ktype;   // Type operations
};

// Example: /sys/devices/ → devices_kset
//          /sys/bus/ → bus_kset

Device / Driver / Bus

struct device

// include/linux/device.h
struct device {
    struct kobject      kobj;               // sysfs representation
    struct device       *parent;            // Parent device (e.g., PCI bridge)
    const char          *init_name;
    struct bus_type     *bus;               // Bus it belongs to
    struct device_driver *driver;           // Bound driver
    void                *driver_data;       // Driver-specific private data
    struct device_node   *of_node;          // DT node
    u64                 *dma_mask;          // DMA addressing range
    const struct attribute_group **groups;  // sysfs attribute groups
    void (*release)(struct device *dev);    // Mandatory! (release callback)
};

struct device_driver

struct device_driver {
    const char              *name;
    struct bus_type         *bus;
    int (*probe)(struct device *dev);           // Probe: driver binds to device
    void (*remove)(struct device *dev);         // Remove: unbind
    void (*shutdown)(struct device *dev);
    const struct of_device_id *of_match_table;  // DT matching
    const struct acpi_device_id *acpi_match_table;
    const struct attribute_group **groups;
};

struct bus_type

struct bus_type {
    const char      *name;                      // PCI, USB, platform, ...
    int (*match)(struct device *dev, struct device_driver *drv);
    int (*probe)(struct device *dev);           // Bus-level probe
    struct subsys_private *p;                   // Device list + driver list
};

Binding Flow

// drivers/base/dd.c
// Device registration:
device_register(dev)device_add(dev)bus_add_device(dev)     // Attach to bus->p->klist_devices
bus_probe_device(dev)device_initial_probe(dev)__device_attach(dev, true)bus_for_each_drv(dev->bus, ...)  // Iterate through all registered drivers
driver_match_device(drv, dev)  // bus->match() or of_match/acpi_match
device_driver_attach(drv, dev)driver_probe_device(drv, dev)really_probe(dev, drv)call_driver_probe(dev, drv) → drv->probe(dev)

Key Point: Binding is bidirectional—when a new device arrives, it iterates through drivers; when a new driver registers, it iterates through devices. Regardless of who registers first, the match will occur.


sysfs and Attributes

// Each directory under /sys = one kobject
// Attribute files = struct device_attribute
struct device_attribute {
    struct attribute attr;
    ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
    ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
};

// Macros: DEVICE_ATTR_RO(name) → generates read-only attribute
//         DEVICE_ATTR_WO(name) → write-only
//         DEVICE_ATTR_RW(name) → read-write

Device Tree vs ACPI

Device Tree / ACPI / PCIe: Different hardware description methods, different matching mechanisms ARM/RISC-V · Device Tree Static description of hardware topology Passed to kernel by bootloader platform_device binds to DT node of_match_table Matches compatible strings x86 · ACPI Provided by UEFI/BIOS Firmware dynamically describes hardware + Power management methods acpi_match_table Matches _HID / _CID PCIe · Self-Enumeration Regardless of architecture Does not rely on DT / ACPI description Bus enumerates devices itself vendor:device ID Matches pci_device_id Different hardware description methods, but the destination is the same: all must fall back to their respective match_table to complete device-driver matching

udev

udev: Processing chain from kernel uevent to device nodes/rule application Kernel detects event Generates uevent netlink socket Kernel → User-space channel udev daemon Receives uevent Load kernel module Based on modalias Create device node /dev/sda, etc. Apply custom rules Permissions / Symlinks The kernel only sends uevent notifications; module loading, device node creation, and rule application are all handled by user-space udev

References

  • Source Code: drivers/base/core.c, drivers/base/bus.c, drivers/base/dd.c, include/linux/device.h
  • Kernel Documentation: Documentation/driver-api/driver-model/

Keywords: kobject, device, driver, bus, probe, sysfs, device tree, ACPI, udev, modalias