Skip to main content

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.

The Linux Device Model (LDM) is a unified data model that describes every device in the system and the relationships between devices, the buses they sit on, and the drivers that manage them. Introduced to replace the collection of ad-hoc per-bus data structures that existed before kernel 2.6, the LDM gives the kernel a single coherent view of the hardware tree. It powers sysfs, hot-plug events, power management, and driver binding from one shared infrastructure.

struct device

struct device is the fundamental object representing a hardware or virtual device. Every higher-level device structure — struct pci_dev, struct platform_device, struct usb_device — embeds a struct device inside it. The bus or subsystem layer owns the outer structure; the driver core owns the embedded struct device.
/* From include/linux/device.h (lines 628–743) */
struct device {
	struct kobject kobj;
	struct device		*parent;

	struct device_private	*p;

	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	const struct bus_type	*bus;    /* type of bus device is on */
	struct device_driver	*driver; /* which driver has allocated this device */
	void		*platform_data;  /* platform-specific data */
	void		*driver_data;    /* driver data, use dev_set/get_drvdata() */

	struct mutex		mutex;

	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

	u64		*dma_mask;
	u64		coherent_dma_mask;

	struct device_node	*of_node;   /* associated device tree node */
	struct fwnode_handle	*fwnode;    /* firmware device node */

	dev_t			devt;       /* creates the sysfs "dev" */

	const struct class	*class;
	const struct attribute_group **groups;

	void (*release)(struct device *dev);
};
Key fields to understand:
  • parent — the device higher in the hierarchy (e.g., the USB hub for a USB device).
  • bus — the bus type this device belongs to; set by the bus layer.
  • driver — populated by the driver core when a driver is successfully bound.
  • platform_data — board-specific data supplied before probe() runs.
  • driver_data — private data stored by the driver, accessed with dev_set_drvdata() and dev_get_drvdata().
  • of_node — the Device Tree node, used to read hardware description properties.
  • release — called when the device’s reference count drops to zero; always set this before registering a device.
Use device_register() to add a device to the model and device_unregister() to remove it.
int device_register(struct device *dev);
struct device *get_device(struct device *dev);
void put_device(struct device *dev);

struct device_driver

struct device_driver represents a driver as a whole — not a specific device instance. It is a statically allocated structure. All bus-specific driver structures (e.g., struct pci_driver, struct platform_driver) embed a struct device_driver.
/* From include/linux/device/driver.h (lines 98–131) */
struct device_driver {
	const char		*name;
	const struct bus_type	*bus;

	struct module		*owner;
	const char		*mod_name;

	bool suppress_bind_attrs;
	enum probe_type probe_type;

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

	int (*probe) (struct device *dev);
	void (*sync_state)(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);
	const struct attribute_group **groups;
	const struct attribute_group **dev_groups;

	const struct dev_pm_ops *pm;
	void (*coredump) (struct device *dev);
};
Register and unregister a driver with:
/* From include/linux/device/driver.h */
int __must_check driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
Bus-specific drivers should use their bus’s registration function (e.g., platform_driver_register(), pci_register_driver()) rather than calling driver_register() directly. These wrappers fill in the bus field and handle bus-specific setup automatically.

How sysfs exposes devices

sysfs is a virtual filesystem that reflects the kernel’s device hierarchy. It is mounted at /sys and is populated automatically by the driver core whenever you register a device or driver. The physical device hierarchy appears under /sys/devices/. Every struct device gets its own directory, nested under its parent:
/sys/devices/
└── platform/
    └── serial8250/
        ├── driver -> ../../../../bus/platform/drivers/serial8250
        ├── power/
        └── uevent
Buses expose two additional views under /sys/bus/<bus-name>/:
/sys/bus/pci/
├── devices/
│   ├── 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0
│   └── 0000:00:01.0 -> ../../../devices/pci0000:00/0000:00:01.0
└── drivers/
    ├── e1000
    └── ahci
You export device attributes to sysfs using DEVICE_ATTR_RO(), DEVICE_ATTR_RW(), or the lower-level DEVICE_ATTR() macro:
/* From include/linux/device.h */
#define DEVICE_ATTR(_name, _mode, _show, _store) \
	struct device_attribute dev_attr_##_name = \
		__ATTR(_name, _mode, _show, _store)

#define DEVICE_ATTR_RO(_name) \
	struct device_attribute dev_attr_##_name = __ATTR_RO(_name)

#define DEVICE_ATTR_RW(_name) \
	struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
Group attributes together and attach them to a device before calling device_register():
static DEVICE_ATTR_RO(temperature);
static DEVICE_ATTR_RW(mode);

static struct attribute *mydev_attrs[] = {
	&dev_attr_temperature.attr,
	&dev_attr_mode.attr,
	NULL,
};

static const struct attribute_group mydev_group = {
	.attrs = mydev_attrs,
};

static const struct attribute_group *mydev_groups[] = {
	&mydev_group,
	NULL,
};

/* Before device_register(): */
dev->groups = mydev_groups;

Bus types and registration

Each bus in the kernel — PCI, USB, I2C, platform — declares a struct bus_type and registers it with bus_register().
/* From include/linux/device/bus.h (lines 83–114) */
struct bus_type {
	const char		*name;
	const char		*dev_name;
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;

	int (*match)(struct device *dev, const struct device_driver *drv);
	int (*uevent)(const struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	void (*sync_state)(struct device *dev);
	void (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;

	bool driver_override;
	bool need_parent_lock;
};

int __must_check bus_register(const struct bus_type *bus);
void bus_unregister(const struct bus_type *bus);
The match() callback is the most important field. It receives a device and a driver and returns a positive value when they are compatible. For example, the platform bus matches by comparing platform_device.name against platform_driver.driver.name or entries in the driver’s of_match_table. You can iterate over all devices or drivers on a bus using:
/* From include/linux/device/bus.h */
int bus_for_each_dev(const struct bus_type *bus, struct device *start,
		     void *data, device_iter_t fn);

int bus_for_each_drv(const struct bus_type *bus, struct device_driver *start,
		     void *data, int (*fn)(struct device_driver *, void *));

Power management integration

struct dev_pm_ops defines the full set of power management callbacks a driver can implement. You attach a dev_pm_ops to a driver via device_driver.pm or, for bus-level control, via bus_type.pm.
/* From include/linux/pm.h (lines 288–312) */
struct dev_pm_ops {
	int (*prepare)(struct device *dev);
	void (*complete)(struct device *dev);
	int (*suspend)(struct device *dev);
	int (*resume)(struct device *dev);
	int (*freeze)(struct device *dev);
	int (*thaw)(struct device *dev);
	int (*poweroff)(struct device *dev);
	int (*restore)(struct device *dev);
	int (*suspend_late)(struct device *dev);
	int (*resume_early)(struct device *dev);
	/* ... noirq variants ... */
	int (*runtime_suspend)(struct device *dev);
	int (*runtime_resume)(struct device *dev);
	int (*runtime_idle)(struct device *dev);
};
For most drivers, you only need suspend and resume. The DEFINE_SIMPLE_DEV_PM_OPS() macro constructs a dev_pm_ops that uses the same callbacks for system sleep and hibernation:
/* From include/linux/pm.h */
#define DEFINE_SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
	_DEFINE_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL)
Use it in your driver like this:
static int mydrv_suspend(struct device *dev)
{
	/* quiesce hardware */
	return 0;
}

static int mydrv_resume(struct device *dev)
{
	/* restore hardware */
	return 0;
}

static DEFINE_SIMPLE_DEV_PM_OPS(mydrv_pm_ops, mydrv_suspend, mydrv_resume);

static struct platform_driver mydrv = {
	.driver = {
		.name = "mydrv",
		.pm   = &mydrv_pm_ops,
	},
	/* ... */
};
Device links express supplier–consumer relationships between devices. They ensure that a supplier device is probed before its consumers and is not removed while consumers are still active. You create a link from the consumer to the supplier inside the consumer’s probe():
/* From include/linux/device.h */
struct device_link *device_link_add(struct device *consumer,
				    struct device *supplier,
				    u32 flags);
void device_link_del(struct device_link *link);
The DL_FLAG_AUTOREMOVE_CONSUMER flag automatically removes the link when the consumer is unbound. The DL_FLAG_PM_RUNTIME flag integrates the link into runtime PM so that the supplier is resumed before the consumer.
Prefer device links over open-coding dependency checks in probe(). Links integrate with deferred probing: if the supplier is not yet probed, adding the link returns an error that you can convert into -EPROBE_DEFER.

Driver model overview

Learn about driver types, the probe/remove lifecycle, and the key headers every driver includes.

Write your first driver

Implement a character device driver and a platform driver with complete code examples.

Build docs developers (and LLMs) love