struct device
struct deviceis the lowest common denominator of the Linux device model: every piece of hardware in the system — a PCI card, a USB webcam, an I²C temperature sensor, a clock on a System-on-Chip (SoC) — is represented, somewhere, by an instance of it. The kerneldoc puts it plainly: “At the lowest level, every device in a Linux system is represented by an instance of struct device” (device.h, v6.12). It is the node the driver core threads onto the unified object graph — it carries thekobjectthat gives it a reference count and a/sysdirectory, the back-pointers to thebusit sits on and thedriverbound to it, thedev_tthat mints a/devnode, the DMA masks the device’s hardware imposes, and the power-management state the suspend/resume machinery walks. Crucially, you almost never see a barestruct device: it is embedded inside a larger, bus-specific structure (struct pci_dev,struct platform_device,struct usb_device), and code climbs from the generic device back up to the specific type withcontainer_of.
This note covers the data structure and its registration lifecycle. Its functional grouping cousin — struct class — is in Device Classes; the bus it points at is in struct bus_type and Bus Registration; the attribute files it sprouts in sysfs are in Device Attributes and sysfs Files; and the whole model it belongs to is in The Linux Device Model.
Mental Model
Think of struct device as a standard electrical connector bolted onto every device-specific structure. The connector is identical everywhere, so the driver core can plug any device into the same machinery: refcount it, give it a sysfs directory, match it to a driver, suspend it. The device-specific structure around it (a pci_dev knows about BARs and config space; a platform_device knows about resources and a Device-Tree node) is the part the bus layer cares about. The generic core only ever touches the connector.
flowchart TB subgraph WRAP["Bus-specific wrapper (one allocation)"] direction TB PCI["struct pci_dev<br/>vendor, device, BARs, config space"] DEV["struct device dev<br/>(embedded — the 'connector')"] PCI -.->|"contains"| DEV end DEV -->|"kobj"| KO["struct kobject<br/>refcount + /sys dir"] DEV -->|"bus"| BUS["struct bus_type<br/>(pci_bus_type)"] DEV -->|"driver"| DRV["struct device_driver<br/>(once bound)"] DEV -->|"class"| CLS["struct class<br/>(optional functional group)"] DEV -->|"devt"| NODE["/dev node<br/>(if MAJOR(devt) != 0)"] DEV -->|"parent"| PARENT["parent struct device<br/>(forms the tree)"] DEV -->|"power"| PM["struct dev_pm_info<br/>suspend/resume/runtime PM"] DRV -.->|"to_pci_dev(dev) = container_of"| PCI
The embedded-connector model. What it shows: a single allocation of struct pci_dev contains a struct device field; the generic driver core only ever holds a struct device *, while the PCI bus layer recovers the full pci_dev with container_of. The device fans out to a kobject (refcount + sysfs), its bus, its bound driver, an optional class, an optional /dev node, its parent in the tree, and its power state. The insight: the generic core and the bus layer share the same memory but see different “shapes” of it — that is exactly what lets one device model serve PCI, USB, platform, and I²C without the core knowing anything bus-specific.
What the Structure Actually Contains
The full definition lives at struct device in device.h (line 721 in v6.12). It is large — dozens of fields, many guarded by #ifdef CONFIG_* — but a handful do the conceptual heavy lifting. Walking them in the order they matter:
struct kobject kobj — the very first field. A kobject is the kernel’s generic reference-counted object: it carries a kref (the atomic reference count), a name, a parent pointer, and the hook that makes a directory appear under /sys. Because the device embeds a kobject, the device inherits all of that for free: get_device()/put_device() are thin wrappers over kobject_get()/kobject_put(), and the device’s sysfs directory is the kobject’s directory. This is the single most important structural fact about the device model — see The Linux Device Model for why “everything is a kobject” is the organizing principle.
struct device *parent — the device this one is attached to, “in most cases … some sort of bus or host controller” (kerneldoc). A USB mouse’s parent is the USB hub port; the hub’s parent is the USB controller; the controller’s parent is a PCI device; and so on up to a virtual root. A NULL parent makes the device top-level — “not usually what you want,” the docs warn. These parent links are what give /sys/devices/ its deep tree shape and what the power-management core walks to suspend children before parents. The parent/child relationship is detailed in The Device Hierarchy and Parent-Child Relationships.
const struct bus_type *bus — which bus this device sits on (pci, usb, i2c, platform, …). The bus owns the .match() and .probe() logic that pairs the device with a driver. See struct bus_type and Bus Registration.
struct device_driver *driver — which driver is currently bound, or NULL if none. Set by the core when .probe() succeeds, cleared on unbind. A subtlety the kernel itself flags: this pointer “can change to NULL underneath us because of unbinding,” so the core reads it with READ_ONCE() in dev_driver_string() (core.c). Driver binding is covered in Driver Binding and the Probe Flow.
void *driver_data — a single opaque pointer the bound driver owns. The core “doesn’t touch it.” This is where a driver stashes its per-device private state — typically a pointer to a kzalloc’d struct holding register base addresses, IRQ numbers, ring buffers, whatever. It is set and read only through the accessors dev_set_drvdata()/dev_get_drvdata() (more below); driver code is not supposed to poke dev->driver_data directly.
void *platform_data — legacy board-specific data, distinct from driver_data. On embedded and SoC systems before Device Tree took over, board files pointed platform_data at structures describing wiring (“what ports are available, chip variants, which GPIO pins act in what additional roles”). It shrank the “Board Support Packages” of the pre-DT era. Modern code prefers Device-Tree / fwnode properties over platform_data — see Platform Devices and Drivers.
dev_t devt — the device number (a packed major:minor, dev_t). If non-zero (MAJOR(devt) != 0), the core treats this device as having a character/block node: during device_add() it creates the sysfs dev file and calls devtmpfs_create_node() to materialize the /dev entry. If devt is 0:0 there is no /dev node. The major/minor split is the subject of Major and Minor Numbers; the kernel-maintained /dev is devtmpfs and the dev Directory.
const struct class *class — an optional pointer to the device’s functional class (e.g. net, block, tty, leds). When set, the device gets a symlink under /sys/class/<name>/, and udev keys off it. A device can be on a bus, in a class, or both.
const struct attribute_group **groups — a NULL-terminated array of attribute groups. The core creates these sysfs files before it fires the KOBJ_ADD uevent, which is precisely why drivers must declare attributes through groups (or dev_groups on the class/bus) rather than calling device_create_file() later — userspace would never be notified of the late files (device.rst). The attribute machinery is Device Attributes and sysfs Files.
struct device_node *of_node / struct fwnode_handle *fwnode — the firmware description of this device. of_node is the Device-Tree node (Open Firmware lineage, hence “of”); fwnode is the firmware-agnostic handle that abstracts over Device Tree and ACPI, so generic property code (device_property_read_u32() etc.) works regardless of the firmware type. See The Unified Device Property Interface fwnode.
u64 *dma_mask, u64 coherent_dma_mask, const struct dma_map_ops *dma_ops, u64 bus_dma_limit — the DMA addressing constraints of the hardware. dma_mask is the range of physical (or IOMMU-translated) addresses the device can reach for streaming mappings; coherent_dma_mask the same for coherent allocations (it is separate because “not all hardware supports 64-bit addresses for consistent allocations”). dma_ops is the per-device set of map/unmap callbacks the DMA API dispatches through. These fields are why the DMA API needs only a struct device * to know how a given device can talk to RAM.
struct dev_pm_info power / struct dev_pm_domain *pm_domain — the power-management state block. power holds runtime-PM counters, the suspend/resume status, wakeup info; pm_domain plugs in extra callbacks for a power domain. The PM core walks the device tree using these. See Runtime Power Management in Drivers and Linux Power Management MOC.
struct dev_links_info links — the supplier/consumer device links for this device, making probe-, suspend-, and shutdown-ordering dependencies explicit rather than guessed from the parent tree.
void (*release)(struct device *dev) — the destructor, invoked when the last reference is dropped (the kobject’s refcount hits zero). This must be set by whoever allocated the device — typically the bus driver that discovered it — and is where the wrapping structure is freed. The kernel is emphatic that you never kfree() a device directly; you put_device() and let release run when the refcount truly reaches zero, because outstanding references (a sysfs reader, an open file) can outlive your code’s interest in the object.
const struct device_type *type — an optional pointer to a struct device_type, a lighter-weight grouping than a class that bundles shared groups, a uevent callback, a devnode callback, a release, and pm ops. The PCI subsystem, for example, uses device types to distinguish a regular device from a bridge.
Uncertain
Verify: the precise set of
#ifdef-guarded DMA fields (dma_io_tlb_pools,dma_iommu,dma_skip_sync) present in 6.18 LTS, and whether any field above was renamed/added between 6.12 and 6.18. Reason: the field walk here is pinned to the v6.12 source blob; the DMA-related members in particular churn release-to-release. To resolve: diffinclude/linux/device.hbetween thev6.12andv6.18tags. uncertain
Embedding: the Container-of Pattern
The kerneldoc states the rule directly: “it is rare for devices to be represented by bare device structures; instead, that structure, like kobject structures, is usually embedded within a higher-level representation of the device” (device.h). Concretely, struct platform_device (platform_device.h, v6.12) looks like:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev; /* <-- the embedded generic device */
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;
struct resource *resource;
...
};struct device dev is statically embedded (not a pointer), so discovering a device is a single allocation — the overview doc calls this out for pci_dev as well: “only one allocation on device discovery.” To recover the platform_device from a generic struct device *, the bus layer uses the container_of upcast wrapped in a named helper:
#define to_platform_device(x) container_of((x), struct platform_device, dev)container_of(ptr, type, member) computes (char *)ptr - offsetof(type, member) and casts the result — given a pointer to the dev member, it returns a pointer to the enclosing struct. Two deliberate design choices flow from this. First, the struct device is not at offset zero of the wrapper (“not necessarily defined at the front”) — the overview doc says this is “to make people think about what they’re doing … and to discourage meaningless and incorrect casts.” Second, device drivers should not reach through dev into generic fields; only the bus layer does, so that “when a field was renamed or removed, every downstream driver” does not break — only the one bus layer changes. This is the encapsulation that lets the device model evolve.
The same shape recurs everywhere: to_pci_dev(dev), to_usb_device(dev), to_i2c_client(dev). Learn it once for struct device and it generalizes to every kobject-embedding structure in the kernel.
The Registration Lifecycle: initialize → add → register
A device comes to life in two steps, which device_register() simply chains (core.c):
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}device_initialize(dev) sets up the in-memory object without making it visible. It assigns the device to the devices_kset, calls kobject_init() (which sets the refcount to 1 and wires the device_ktype), initializes the dma_pools list, the mutex, the devres spinlock and list, the PM state (device_pm_init), the NUMA node, and the supplier/consumer link lists. After this the device has a refcount and is a valid object, but it is not in any list, has no sysfs directory, and fires no uevent.
device_add(dev) is where the device becomes real and visible. Its essential sequence (eliding the error-unwind labels):
- Resolve the device’s name: from
init_name, or fromdev->bus->dev_name + id, failing with-EINVALif neither yields a name. - Get the parent reference and compute the kobject parent (
get_device_parent), thenkobject_add()— this is the moment the/sysdirectory appears. - Create the
ueventattribute file, thendevice_add_class_symlinks()(the/sys/class/<class>/<name>symlink for classed devices), thendevice_add_attrs()(thegroupsfiles). bus_add_device()— link the device into its bus’s device list and create the bus symlinks.dpm_sysfs_add()+device_pm_add()— register with the power-management core.- If
MAJOR(dev->devt)is set: create thedevattribute file, the/sys/dev/...entry, and calldevtmpfs_create_node()to make the/devnode. bus_notify(BUS_NOTIFY_ADD_DEVICE)thenkobject_uevent(&dev->kobj, KOBJ_ADD)— broadcast the “new device” uevent to userspace (udev). All attributes must already exist by now, which is why steps 3–6 precede this.- Process firmware device links (
fw_devlink_link_device), thenbus_probe_device(dev)— kick off driver matching and probe. - Add the device to its parent’s children list and to its class’s device list, notifying any registered
class_interfaces.
The two-step split exists, the kerneldoc explains, for the rare case where you need to “use and refcount the device before it is added to the hierarchy” — most code just calls device_register().
Teardown mirrors this: device_unregister() = device_del() + put_device(). device_del() removes the sysfs files, fires KOBJ_REMOVE, unbinds the driver, and unlinks from bus/class/parent lists; put_device() drops the reference taken at init. The object is only actually freed (via release) when the last reference goes — the kerneldoc’s “rule of thumb: if device_add() succeeds, call device_del(); if it has not succeeded, use only put_device().”
Driver Data Accessors and Device Logging
Two small but ubiquitous API families round out the structure.
Driver-private data is read and written exclusively through trivial inline accessors (device.h):
static inline void *dev_get_drvdata(const struct device *dev)
{
return dev->driver_data;
}
static inline void dev_set_drvdata(struct device *dev, void *data)
{
dev->driver_data = data;
}The indirection looks pointless — they just touch dev->driver_data — but it is the encapsulation contract: drivers go through the accessors so the field can move or grow logic later without touching every driver. A typical .probe() does priv = devm_kzalloc(...); dev_set_drvdata(dev, priv); and every other callback recovers state with priv = dev_get_drvdata(dev);. (Bus-specific wrappers exist too: platform_set_drvdata(), pci_set_drvdata() ultimately call dev_set_drvdata() on the embedded device.)
Device-aware logging is the dev_printk family (dev_printk.h, v6.12). Instead of bare printk, drivers use:
dev_emerg(dev, fmt, ...) /* KERN_EMERG */
dev_alert(dev, fmt, ...) /* KERN_ALERT */
dev_crit(dev, fmt, ...) /* KERN_CRIT */
dev_err(dev, fmt, ...) /* KERN_ERR */
dev_warn(dev, fmt, ...) /* KERN_WARNING*/
dev_notice(dev, fmt, ...) /* KERN_NOTICE */
dev_info(dev, fmt, ...) /* KERN_INFO */
dev_dbg(dev, fmt, ...) /* dynamic debug */The payoff is the automatic prefix. The core’s __dev_printk() formats the line as "%s %s: <message>" using dev_driver_string(dev) and dev_name(dev) (core.c). dev_driver_string() returns the bound driver’s name if any, else the bus name, else the class name. So dev_err(&pdev->dev, "init failed: %d\n", ret) produces a kernel-log line like:
mydriver 10000000.serial: init failed: -22
— self-identifying, greppable, and unambiguous even when fifty identical devices exist. This is why dev_err(dev, ...) is strongly preferred over pr_err(...) in driver code. The dev_* macros also feed structured metadata (subsystem, device) into the printk record via dev_printk_emit(), which userspace log tooling can filter on.
Failure Modes and Common Misunderstandings
Freeing a device with kfree() instead of put_device(). The single most dangerous mistake. References to a device can outlive your driver’s interest — a userspace process with /sys/.../some_attr open, a sibling holding a device link. If you kfree() the wrapper while a reference exists, the next put_device() runs release on freed memory (use-after-free). The discipline: set dev->release to free the wrapper, and only ever put_device().
Forgetting to set release. If device_register() succeeds but dev->release is NULL, the core emits a loud warning (“Device ’…’ does not have a release() function, it is broken and must be fixed”) when the device is removed, and leaks the memory. Every dynamically allocated device needs a release callback; helpers like device_create() set one for you.
Adding attributes after KOBJ_ADD. Calling device_create_file() in .probe() (after the device is registered) creates sysfs files that udev never hears about, because the KOBJ_ADD uevent already fired. The device.rst doc warns of exactly this: “userspace won’t get notified and userspace will not know about the new attributes.” The fix is to attach attributes via the groups/dev_groups pointer so they exist before the uevent.
Touching generic struct device fields from a leaf driver. Reaching dev->kobj or dev->p from an ordinary PCI driver couples that driver to driver-core internals the overview doc explicitly tells you to leave to the bus layer. It compiles, but it breaks the moment the core refactors a field.
Reading dev->driver without care. Because unbind can NULL it concurrently, code that dereferences dev->driver outside the device lock can race; the core itself uses READ_ONCE().
Alternatives and When You Touch It Directly
Most driver authors never call device_register() themselves — they register a struct pci_driver, struct platform_driver, or struct i2c_driver, and the bus core creates and registers the embedded struct device when it enumerates hardware. You receive a fully-formed struct device * in your .probe(). You allocate and register a device manually only when you are:
- Writing a bus or class core that discovers devices (the PCI layer minting a
pci_devper slot). - Creating a synthetic device — e.g.
device_create()to get a/devnode and a/sys/classentry without a real bus underneath (the misc and many char-device frameworks do this; see The miscdevice Framework for the most ergonomic path to a single character device). - Using
root_device_register()to create a virtual parent under/sys/devices/for a family of synthetic devices.
When you only need one character device node, the miscdevice framework hides almost all of this — you fill a struct miscdevice and the framework handles device_create() and the minor allocation. Reach for raw struct device registration only when miscdevice or a real bus does not fit.
Production Notes
In real subsystems the registration is wrapped but the struct device is unmistakable. The character-memory driver drivers/char/mem.c (v6.12) registers a class and then, for each minor, calls device_create(&mem_class, NULL, MKDEV(MEM_MAJOR, minor), NULL, devlist[minor].name) — that single call allocates a struct device, sets its class, devt, and release, and runs the full device_add() flow, producing /dev/null, /dev/zero, /dev/mem, and friends along with their /sys/class/mem/* entries. The LED subsystem (drivers/leds/led-class.c) does the same via device_create_with_groups(), attaching per-LED attribute groups in the same atomic step.
The modern allocation idiom inside .probe() pairs struct device with managed resources: devm_kzalloc(dev, ...) for private state and devm_* for IRQs, clocks, and mappings, all tied to the device’s bound lifetime so they auto-release on unbind (Managed Device Resources devres). This is why correct manual error-path cleanup matters far less in 6.x driver code than it did a decade ago — the device’s devres_head list does the unwinding.
See Also
- The Linux Device Model — the parent concept: the kobject-rooted object graph this structure is a node in
- Device Classes —
struct class, the functional groupingdev->classpoints at - struct bus_type and Bus Registration — the bus
dev->buspoints at, owner of.match()/.probe() - struct device_driver — the driver
dev->driverbinds to - Driver Binding and the Probe Flow — what
bus_probe_device()triggers afterdevice_add() - Device Attributes and sysfs Files — the
groupsfiles and theDEVICE_ATTRmacros - The Device Hierarchy and Parent-Child Relationships — what
parentbuilds - kobject kset and ktype — the embedded
kobjthat gives the device its refcount and sysfs dir - Reference Counting with kref — the refcount discipline behind
get_device/put_device - The miscdevice Framework — the easy path when you just need one character device
- Managed Device Resources devres —
devm_*, the modern resource-lifetime style - Linux Device Drivers and Device Model MOC — the parent map