↓ Skip to main content

Understanding Peripheral Drivers Through the Linux Kernel Device Model

1. Introduction: Object-Oriented Thinking in the Kernel
#

Modern Linux treats devices, buses, and drivers using a highly object-oriented architecture. The kernel models system components using reusable abstractions that resemble class inheritance:

  • kobject → fundamental building block
  • kset → grouping mechanism
  • device / driver / bus → higher-level abstractions
  • subsystems → real-world peripherals such as PCI, USB, I2C

This design establishes a clean hierarchy that allows the kernel to manage the complexity of heterogeneous hardware in a consistent way.

2. The Foundation: kobject — The Core Kernel Object
#

At the base of Linux’s device model is kobject, a small but crucial structure embedded inside most driver-related data structures. A kobject provides:

  • Naming
  • Reference counting
  • Parent/child hierarchy
  • Sysfs linkage
  • Type information (kobj_type)

struct kobject
#

struct kobject {
    const char *name;
    struct list_head entry;
    struct kobject *parent;      // Parent in the object hierarchy
    struct kset *kset;
    struct kobj_type *ktype;     // Type definition and callbacks
    struct kernfs_node *sd;      // Sysfs directory entry
    struct kref kref;            // Reference counting
    unsigned int state_initialized:1;
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    unsigned int uevent_suppress:1;
};

struct kobj_type
#

This structure defines the behavior of a kobject, including sysfs operations and release logic.

struct kobj_type {
    void (*release)(struct kobject *kobj);
    const struct sysfs_ops *sysfs_ops;
    const struct attribute_group **default_groups;
    const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *);
    const void *(*namespace)(struct kobject *);
    void (*get_ownership)(struct kobject *, kuid_t *, kgid_t *);
};

Together, kobject + kobj_type implement the kernel’s base class pattern.

3. Building Up: Devices, Drivers, and Buses
#

From this foundation, Linux constructs the three central abstractions:

  • struct device — represents a hardware device
  • struct device_driver — represents a driver that manages devices
  • struct bus_type — represents a physical or logical bus that connects devices and drivers

3.1 struct device — The Universal Device Object
#

Every device in the kernel—from PCI devices to USB endpoints—eventually becomes a struct device.

struct device {
    struct device *parent;
    struct kobject kobj;           // Embedded kobject
    const char *init_name;
    const struct device_type *type;

    struct bus_type *bus;          // Bus this device is attached to
    struct device_driver *driver;  // Bound driver

    struct device_node *of_node;   // Device Tree node (if applicable)
    dev_t devt;                     // Char/block device number

    struct class *class;           // Functional grouping (input, net, etc.)
    struct klist_node knode_class;

    void (*release)(struct device *dev);
};

3.2 struct device_driver — The Driver Abstraction
#

A driver contains probe/remove callbacks and its own embedded kobject.

struct device_driver {
    const char *name;
    struct bus_type *bus;

    const struct of_device_id   *of_match_table;
    const struct acpi_device_id *acpi_match_table;

    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    struct driver_private *p;
};

3.3 struct bus_type — Matching Devices and Drivers
#

The bus provides the matching logic and dispatches probe/remove to the driver.

struct bus_type {
    const char *name;
    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);

    struct subsys_private *p;
};

Examples of kernel buses:

  • PCI
  • USB
  • I2C
  • SPI
  • Platform bus

4. Extending the Model: Subsystems and Real Hardware
#

Every real hardware subsystem extends the generic device and driver structures by embedding them inside richer, bus-specific structures.

4.1 PCI — struct pci_dev
#

struct pci_dev {
    struct list_head bus_list;
    struct pci_bus *bus;
    struct pci_bus *subordinate;
    struct device dev;     // Generic device embedded here
};

The kernel uses container_of() macros such as to_pci_dev() to recover the extended structure.

4.2 USB — struct usb_device
#

USB follows the same pattern:

  • usb_device embeds a struct device
  • Matching happens via USB IDs
  • Drivers embed struct device_driver

4.3 I2C — struct i2c_client
#

I2C devices are represented as:

struct i2c_client {
    unsigned short addr;
    struct device dev;   // Embedded generic device
    ...
};

Each bus follows the same expansion structure:

  1. Base object (device)
  2. Bus-specific extended object (pci_dev, usb_device, i2c_client)
  3. Bus-specific driver (pci_driver, usb_driver, i2c_driver)
  4. Containment and inheritance via embedded kobject

5. Summary: A Unified, Object-Oriented Device Model
#

The Linux kernel’s device model provides:

  • A consistent framework for representing hardware
  • Reusable abstractions (kobject, kset)
  • Logical inheritance via embedded structures
  • Unified matching logic across PCI, USB, I2C, SPI, etc.
  • sysfs exposure of all device relationships

This object-oriented approach allows Linux to support a vast range of peripherals through the same set of abstractions, enabling maintainability, extensibility, and a clear hierarchical view of complex hardware systems.

Related