Timeline
Timeline
2025-12-01
init
This article introduces the basic concepts and core functions of the Linux device model. It points out that the character device driver framework is difficult to meet the requirements of complex functions such as power management and hot-plugging, while the Linux device model makes driver development more modular and efficient by providing common APIs and mechanisms. The article summarizes four major advantages of the device model: achieving code reuse, supporting dynamic allocation and release of resources, simplifying driver writing, and providing a hot-plug mechanism. It also emphasizes that its design draws on object-oriented thinking, treating devices as objects that can be inherited and extended. In addition, the article focuses on kobject and kset, the basic components of the device model, including the key fields in the kobject structure (such as name, parent-child relationship, reference count, sysfs directory entry, etc.) and the tree-like hierarchical relationship of their corresponding directories in sysfs, and introduces kobject_create_and_add() and other core APIs. The overall content provides clear guidance for Linux driver developers to understand the device model framework.
Linux Driver Notes
| Table of Contents | Links |
|---|---|
| 1. Linux Driver Framework | |
| 2. Linux Driver Loading Logic | |
| 3. Character Device Basics | |
| 4. Concurrency and Race Conditions | |
| 5. Advanced Character Device Topics | |
| 6. Interrupts | |
| 7. Platform Bus | |
| 8. Device Tree | |
| 9. Device Model | |
| 10. Hotplug | |
| 11. pinctrl Subsystem | |
| 12. GPIO subsystem | |
| 13. Input subsystem | |
| 14. 1-Wire | |
| 15. I2C | |
| 16. SPI | |
| 17. UART | |
| 18. PWM | |
| 19. RTC | |
| 20. Watchdog | |
| 21. CAN | |
| 22. Network devices | |
| 23. ADC | |
| 24. IIO | |
| 25. USB | |
| 26. LCD |
Device Model
Character device drivers are usually suitable for relatively simple devices. For some more complex functions, such asPower managementandhot-plug event management, using the character device framework may not be flexible and efficient enough.
To handle more complex devices and functions, the Linux kernel provides a device model. The device model allows developers to describe hardware devices and their relationships in a more advanced way, and provides a set of common APIs and mechanisms to handle device registration, hot-plug events, power management, and so on.
By using the device model, driver developers can leave more low-level functions to the kernel to handle, without having to re-implement these basic functions. This makes driver writing more advanced and modular, reducing duplicate work and the possibility of errors.
For some common hardware devices, such as USB, I2C, and platform devices, the kernel already provides corresponding device models and related drivers. Developers can write drivers based on these models, thereby implementing the functions of specific devices more quickly, and can take advantage of the kernel’s power management and hot-plug event management features.。
Advantages of Using the Device Model
The device model plays an important role in kernel drivers. It provides a unified way to describe hardware devices and their relationships. The following are several important aspects of the device model in kernel drivers.
- Code Reuse:
- Dynamic Allocation and Release of Resources
The device model provides a mechanism to
- Simplify Driver Writing
The device model provides a set of common APIs and mechanisms, making driver writing more simplified and modular. Developers can use these APIs to register devices, handle device events, perform device read/write operations, etc., without having to re-implement these common functions.
- Hot-Plug Mechanism
The device model supports hot-plug mechanism, which can dynamically add or remove devices at runtime. When a device is inserted or removed, the kernel generates corresponding hot-plug events. Drivers can listen to these events to perform corresponding operations, such as device initialization or release.
- Object-Oriented Thinking in Drivers
The design of the device model draws on the ideas of object-oriented programming (OOP).Each device is viewed as an object with its own properties and methods, and can be inherited and extended through the device model mechanism.. This design makes driver development more modular and extensible, allowing it to better handle different types of devices and functional requirements.
kobject and kset
kobjectandksetare basic concepts in the Linux kernel for managing kernel objects.
struct kobject
kobject(kernel object) is a generic object model abstracted in the kernel,used to represent various entities in the kernel。kobjectis a structure that contains some properties and methods describing the object. It provides a unified interface and mechanism for managing and operating kernel objects.
123456789101112131415161718 | // include/linux/kobject.hstruct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct kernfs_node *sd; /* sysfs directory entry */ struct kref kref; struct delayed_work release; 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;}; |
const char *name: represents the name of the kobject, usually used to/syscreate a corresponding directory under the directorystruct list_head entry: used to link the kobject to the child object list of the parent kobject to establish a hierarchical relationship.struct kobject *parent: points to the parent kobject, indicating the hierarchical relationship of the kobject.struct kset *kset: points to the kset that contains the kobject, used to further organize and manage kobjects.struct kobj_type *ktype: points to the kobj_type structure that defines the kobject type,describes the properties and operations of the kobject。struct kernfs_node *sd: points to the correspondingkernfs_node, used to access and operate the sysfs directory entry.struct kref kref:used for reference counting of kobjects, ensuring that resources are correctly released when no longer in use。unsigned intfield: indicates some status flags and configuration options, such as whether it has been initialized, whether it is in sysfs, whether add/remove uevent events have been sent, etc.
Each kobject corresponds to the system/sys/a directory under

Because kobject represents the system/sysa directory under, and directories have multiple levels, so the tree-like relationship of corresponding kobjects is shown in the following figure.

API
kobject_create_and_add()
| Item | Description |
|---|---|
| Function definition | struct kobject *kobject_create_and_add(const char *name, struct kobject *parent); |
| Parameter name | kobject name |
| Parameter parent | parent directory |
| Function | Create + initialize + register kobject, will/syscreate a directory with that name under the directory |
| Return value | kobject pointer |
kobject_init_and_add()
| Item | Description |
|---|---|
| Function definition | int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...); |
| parameter kobj | kobject with allocated memory |
| parameter ktype | type description structure |
| Parameter parent | parent directory |
| Parameter fmt | name format string |
| Function | Initialize + add to sysfs |
| Typical scenarios | Custom structure embedding kobject |
| Return value | 0 success, others failure |
kobject_put()
| Item | Description |
|---|---|
| Function definition | void kobject_put(struct kobject *kobj); |
| Function | decrement reference count |
| Trigger | Count is 0 → release callback |
| Must | Yes |
kobject_get()
| Item | Description |
|---|---|
| Function definition | struct kobject *kobject_get(struct kobject *kobj); |
| Function | increment reference count |
| Purpose | Prevent premature release |
example
There are two ways to create a kobject
123456789101112131415161718192021222324252627282930313233343536373839 | struct kobject *mykobject1;struct kobject *mykobject2;struct kobject *mykobject3;struct kobj_type mytype;static int __init kobject_test_init(void){ int ret = 0; // Create kobject // Method 1: kobject_create_and_add() mykobject1 = kobject_create_and_add("mykobject01", NULL); mykobject2 = kobject_create_and_add("mykobject02", mykobject1); // Method 2: kzalloc() + kobject_init_and_add() mykobject3 = kzalloc(sizeof(struct kobject), GFP_KERNEL); ret = kobject_init_and_add(mykobject3, &mytype, NULL, "%s", "mykobject03"); return ret;}static void __exit kobject_test_exit(void){ kobject_put(mykobject3); kobject_put(mykobject2); kobject_put(mykobject1);}module_init(kobject_test_init);module_exit(kobject_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a test sample for kboject"); |
Test:
1234567 | $ insmod kobject_test.ko[ 12.622003] kobject_test: loading out-of-tree module taints kernel.$ ls /sys/block class devices fs module mykobject03bus dev firmware kernel mykobject01 power$ ls /sys/mykobject01mykobject02 |
struct kset
kset(kset) is a kind of [container] used to organize and manage a group of relatedkobjectcontainer.
ksetYeskobjectan extension of, which provides a hierarchical organizational structure, can organize a group of relatedkobjectorganize together.ksetIn the kernel, usestruct ksetstructure to represent
123456789101112131415161718192021222324 | // include/linux/kobject.h/** * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. * * A kset defines a group of kobjects. They can be individually * different "types" but overall these kobjects all want to be grouped * together and operated on in the same manner. ksets are used to * define the attribute callbacks and other common events that happen to * a kobject. * * @list: the list of all kobjects for this kset * @list_lock: a lock for iterating over the kobjects * @kobj: the embedded kobject for this kset (recursion, isn't it fun...) * @uevent_ops: the set of uevent operations for this kset. These are * called whenever a kobject has something happen to it so that the kset * can add new environment variables, or filter out the uevents if so * desired. */struct kset { struct list_head list; spinlock_t list_lock; struct kobject kobj; const struct kset_uevent_ops *uevent_ops;} __randomize_layout; |
struct list_head list: All kobjects of this kset are connected throughkobject.entrya linked list connecting them.spinlock_t list_lock: Used to protect iterative access to the kobject linked list, ensuring thread safety.struct kobject kobj: As the kobject representation of the kset, used to/syscreate a corresponding directory under the directory, and associate it with the kset.const struct kset_uevent_ops *uevent_ops:A structure pointing to the kset’s uevent operations, used to handle uevent events related to the kset(hotplug-related).
The relationship between kobject and kset is as follows:

- kset is an extension of kobject
kset can be regarded as a special form of kobject, which extends kobject and provides some additional functionality.A kset can contain multiple kobjects, forming a hierarchical organizational structure.。
- A kobject belongs to a kset
A kobject is usually associated with a kset. The field in the kobject structurestruct kset *ksetpoints to the kset it belongs to (can be NULL). This association represents the collection or organization to which the kobject belongs.
Summary: The relationship between kset and kobject is:A kset can contain multiple kobjects, while a kobject can only belong to one kset.。
kset provides collection management and operation interfaces for kobjects, used to organize and manage kobjects with similar characteristics or relationships. This relationship enables the kernel to manage and operate different types of kernel objects in a unified manner.
API
kset_create_and_add()
| Item | Description |
|---|---|
| Function definition | struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent); |
| Header file | #include <linux/kobject.h> |
| Parameter name | kset name (directory name) |
| Parameter uevent_ops | Hotplug event callback (usually NULL) |
| Parameter parent | Parent kobject (NULL → /sys) |
| Function | Create and register a kset,/syscreate a directory with that name under it |
| Effect | Create directory in sysfs |
| Return value | Success: kset pointer; Failure: NULL |
kset_unregister()
| Item | Description |
|---|---|
| Function definition | void kset_unregister(struct kset *k); |
| Function | Unregister kset |
| Effect | Delete sysfs directory |
| Note | Automatically release reference |
example
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 | // Define kobject pointerstruct kobject *mykobject01;struct kobject *mykobject02;// Define kset pointerstruct kset *mykset;// Define kobj_type structurestruct kobj_type mytype;static int __init kset_test_init(void){ int ret; // Create and add mykset mykset = kset_create_and_add("mykset", NULL, NULL); // Create and add mykobject02 mykobject01 = kzalloc(sizeof(struct kobject), GFP_KERNEL); mykobject01->kset = mykset; ret = kobject_init_and_add(mykobject01, &mytype, NULL, "%s", "mykobject01"); // Create and add mykobject01 mykobject02 = kobject_create_and_add("mykobject02", mykobject01); return 0;}static void __exit kset_test_exit(void){ // Release the reference count of mykobject01 kobject_put(mykobject01); // Release the reference count of mykobject02 kobject_put(mykobject02); kset_unregister(mykset);}module_init(kset_test_init);module_exit(kset_test_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("This is a test sample for kset"); |
Test:
123456789101112131415161718192021 | $ insmod kset_test.ko[ 375.127833] kset_test: loading out-of-tree module taints kernel.$ cd sys/sys $ lsblock class devices fs module powerbus dev firmware kernel mykset/sys $ cd mykset//sys/mykset $ lsmykobject01/sys/mykset $ cd mykobject01//sys/mykset/mykobject01 $ lsmykobject02/sys/mykset/mykobject01 $ cd mykobject02//sys/mykset/mykobject01/mykobject02 $ ls/sys/mykset/mykobject01/mykobject02 $ cd /$ rmmod kset_test.ko$ lsmod$ cd sys/sys $ lsblock class devices fs modulebus dev firmware kernel power |
kref reference counter
12345678910111213 | // include/linux/kref.hstruct kref { refcount_t refcount;};// include/linux/refcount.htypedef struct refcount_struct { atomic_t refs;} refcount_t;// include/linux/types.htypedef struct { int counter;} atomic_t; |
When using a reference counter, it is common toembed the kref structure into other structures, for examplestruct kobject, to achieve reference count management.
To implement the reference counting functionality,struct kobjectit usually includes an embeddedstruct krefobject. In this way, bystruct krefoperating on itstruct kobjectthe reference count can be managed, and related resources are released when the reference count decreases to 0.
for examplestruct device_node:
123456789101112131415161718192021 | struct device_node { const char *name; phandle phandle; const char *full_name; struct fwnode_handle fwnode; struct property *properties; struct property *deadprops; /* removed properties */ struct device_node *parent; struct device_node *child; struct device_node *sibling; struct kobject kobj; unsigned long _flags; void *data; unsigned int unique_id; struct of_irq_controller *irq_trans;}; |
Common API functions
kref_init()
Function : Initialize anstruct krefobject, and set its reference count to 1。All reference-counted objects must call this function once before use.
Function prototype
1234 | static inline void kref_init(struct kref *kref){ refcount_set(&kref->refcount, 1);} |
kref_get()
Function: Increment the kref reference count by 1.
Function prototype
1234 | static inline void kref_get(struct kref *kref){ refcount_inc(&kref->refcount);} |
Applicable scenarios: Whenever a structure is held or used by a new user, this function needs to be called to increase the reference count.
kref_put()
Function: reference count -1, when the count reaches zero, callrelease()function to release resources (usually freeing memory).
Function prototype
12345678 | static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)){ if (refcount_dec_and_test(&kref->refcount)) { release(kref); return 1; } return 0;} |
Key points
- reference count becomes 0 → call the callback
release(kref)to perform actual destruction. - Returns 1 to indicate the reference count has become 0 and destruction has been performed.
- Returns 0 to indicate the reference count is > 0 and the object is still valid.
refcount_set()
Function: Set the underlying atomic reference count value.kref_init()This function is called internally.
Function prototype
1234 | static inline void refcount_set(refcount_t *r, int n){ atomic_set(&r->refs, n);} |
How kobject is freed
There are two ways to create a kobject:
- use
kobject_create_and_add()The function creates a kobject:kobject_create_and_add()The function first callskobject_create()function, which useskzalloc()to allocate memory space for the kobject. Inkobject_create()the function, callkobject_init()function initializes the allocated memory and specifies the default ktype. Next,kobject_create_and_add()Function callkobject_add()The function adds the kobject to the system, making it visible.kobject_add()The function internally callskobject_add_internal()function, which is responsible for adding the kobject to the parent object’s child list and creating the corresponding sysfs filesystem entries. - use
kobject_init_and_add()The function creates a kobject:kobject_init_and_add()The function requires manual memory allocation, and throughkobject_init()The function initializes the allocated memory. At this point, you need to implement the ktype structure yourself. After initialization is complete, callkobject_add()The function adds the kobject to the system.
Regardless of the method, ultimately usekobject_put()to release the kobject
12345678910111213141516171819202122232425262728293031323334 | // lib/kobject.c/** * kobject_put() - Decrement refcount for object. * @kobj: object. * * Decrement the refcount, and if 0, call kobject_cleanup(). */void kobject_put(struct kobject *kobj){ if (kobj) { if (!kobj->state_initialized) WARN(1, KERN_WARNING "kobject: '%s' (%p): is not initialized, yet kobject_put() is being called.\n", kobject_name(kobj), kobj); kref_put(&kobj->kref, kobject_release); }}EXPORT_SYMBOL(kobject_put);static void kobject_release(struct kref *kref){ struct kobject *kobj = container_of(kref, struct kobject, kref); unsigned long delay = HZ + HZ * (get_random_int() & 0x3); pr_info("kobject: '%s' (%p): %s, parent %p (delayed %ld)\n", kobject_name(kobj), kobj, __func__, kobj->parent, delay); INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup); schedule_delayed_work(&kobj->release, delay); kobject_cleanup(kobj);} |
It can be seen that when kref is 0, it will callkobject_release(), and this function will callkobject_cleanup()
12345678910111213141516171819202122232425262728293031323334353637383940414243 | // lib/kobject.c/* * kobject_cleanup - free kobject resources. * @kobj: object to cleanup */static void kobject_cleanup(struct kobject *kobj){ struct kobject *parent = kobj->parent; struct kobj_type *t = get_ktype(kobj); const char *name = kobj->name; pr_debug("kobject: '%s' (%p): %s, parent %p\n", kobject_name(kobj), kobj, __func__, kobj->parent); if (t && !t->release) pr_debug("kobject: '%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n", kobject_name(kobj), kobj); /* remove from sysfs if the caller did not do it */ if (kobj->state_in_sysfs) { pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n", kobject_name(kobj), kobj); __kobject_del(kobj); } else { /* avoid dropping the parent reference unnecessarily */ parent = NULL; } if (t && t->release) { pr_debug("kobject: '%s' (%p): calling ktype release\n", kobject_name(kobj), kobj); t->release(kobj); } /* free name if we allocated it */ if (name) { pr_debug("kobject: '%s': free name\n", name); kfree_const(name); } kobject_put(parent);} |
The function internally defines a pointer tostruct kobj_typea pointer t to the structure, used to obtain the type information of kobj
If t exists butt->releaseis NULL, it means that the type of kobj does not define a release function, and a debug message will be printed to indicate this situation.
Then, check the state variable of kobjstate_in_sysfs. If true, it means the caller has not removed kobj from sysfs, and it will automatically callkobject_del()function to remove it from sysfs.
Next, check again whether t exists, and checkt->releasewhether it exists. If it exists, it means the type of kobj defines a release function, and that release function will be called for resource cleanup.
kobject_cleanup()The implementation of the function shows that,the release function ultimately called is defined in the kobj_type structure。
This explains why when usingkobject_init_and_add()the function,struct kobj_typethe reason why the structure cannot be empty.
struct kobj_type
12345678910111213141516171819202122232425262728293031323334 | // include/linux/kobject.hstruct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; /* use default_groups instead */ const struct attribute_group **default_groups; const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);};// lib/kobject.cstruct kobject *kobject_create(void){ struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); if (!kobj) return NULL; kobject_init(kobj, &dynamic_kobj_ktype); return kobj;}// lib/kobject.cvoid kobject_init(struct kobject *kobj, struct kobj_type *ktype){ ... kobject_init_internal(kobj); kobj->ktype = ktype; return; ...}EXPORT_SYMBOL(kobject_init); |
Anddynamic_kobj_ktypeDefined as:
1234567891011 | // lib/kobject.cstatic void dynamic_kobj_release(struct kobject *kobj){ pr_debug("kobject: (%p): %s\n", kobj, __func__); kfree(kobj);}static struct kobj_type dynamic_kobj_ktype = { .release = dynamic_kobj_release, .sysfs_ops = &kobj_sysfs_ops,}; |
Bus, device, driver, class
The device model includes the following four concepts:
- Bus: The bus is a fundamental component in the device model,a communication channel used to connect and transmit data。A bus can be a physical bus (such as PCI, USB) or a virtual bus (such as a virtual device bus). The bus provides the basic mechanism for communication and data transmission between devices.
- Device:A device refers to a hardware device in a computer system, such as a network card, monitor, keyboard, etc. Each device has a unique identifier for identification and management in the system. The device model describes the attributes and characteristics of a device through device descriptors.
- Driver:A driver is a software component in the device model, used to control and manage the operations of a device. Each device requires a corresponding driver to interact and communicate with the operating system. The driver is responsible for sending commands to the device, receiving device events, performing device configuration, and other operations.
- Class: A class is alogical organization unit, used forthat classifies and manages devices with similar functions and characteristics. A class defines a collection of devices that share the same attributes and behaviors. Through device classes, devices can be grouped, identified, and accessed.
In the Linux device model, a virtual bus named “platform” is created to connect some device controllers that are directly connected to the CPU. Such device controllers usually do not conform to common bus standards, such as PCI bus and USB bus, so Linux uses the platform bus to manage these devices.
1. Platform The bus allows device controllers to communicate and interact with device drivers
2. Platform Device controllers are defined in the device tree,and are matched with corresponding device drivers through the device tree
In the device model, the Platform bus provides a unified interface and mechanism to register and manage these device controllers. Device drivers can bind and communicate with the corresponding device controllers by registering with the Platform bus. Device drivers can access the registers of the device controller, configure the device, handle interrupts, and perform other operations, as shown in the following figure:

struct bus_type
The bus_type structure is a data structure in the Linux kernel used to describe a bus
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 | // include/linux/device/bus.h/** * struct bus_type - The bus type of the device * * @name: The name of the bus. * @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id). * @dev_root: Default device to use as the parent. * @bus_groups: Default attributes of the bus. * @dev_groups: Default attributes of the devices on the bus. * @drv_groups: Default attributes of the device drivers on the bus. * @match: Called, perhaps multiple times, whenever a new device or driver * is added for this bus. It should return a positive value if the * given device can be handled by the given driver and zero * otherwise. It may also return error code if determining that * the driver supports the device is not possible. In case of * -EPROBE_DEFER it will queue the device for deferred probing. * @uevent: Called when a device is added, removed, or a few other things * that generate uevents to add the environment variables. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. * @sync_state: Called to sync device state to software state after all the * state tracking consumers linked to this device (present at * the time of late_initcall) have successfully bound to a * driver. If the device has no consumers, this function will * be called at late_initcall_sync level. If the device has * consumers that are never bound to a driver, this function * will never get called until they do. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. * * @online: Called to put the device back online (after offlining it). * @offline: Called to put the device offline for hot-removal. May fail. * * @suspend: Called when a device on this bus wants to go to sleep mode. * @resume: Called to bring a device on this bus out of sleep mode. * @num_vf: Called to find out how many virtual functions a device on this * bus supports. * @dma_configure: Called to setup DMA configuration on a device on * this bus. * @pm: Power management operations of this bus, callback the specific * device driver's pm-ops. * @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU * driver implementations to a bus and allow the driver to do * bus-specific setup * @p: The private data of the driver core, only the driver core can * touch this. * @lock_key: Lock class key for use by the lock validator * @need_parent_lock: When probing or removing a device on this bus, the * device core should lock the device's parent. * * A bus is a channel between the processor and one or more devices. For the * purposes of the device model, all devices are connected via a bus, even if * it is an internal, virtual, "platform" bus. Buses can plug into each other. * A USB controller is usually a PCI device, for example. The device model * represents the actual connections between buses and the devices they control. * A bus is represented by the bus_type structure. It contains the name, the * default attributes, the bus' methods, PM operations, and the driver core's * private data. */struct bus_type { const char *name; //The name of the bus type const char *dev_name;//Bus device name struct device *dev_root;//Root device of the bus device const struct attribute_group **bus_groups;//Bus type attribute group const struct attribute_group**dev_groups;//Device attribute group const struct attribute_group **drv_groups;//Driver attribute group int (*match)(struct device *dev, struct device_driver *drv);//Match function between device and driver int (*uevent)(struct device *dev, struct kobj_uevent_env *env);//Event handler function of the device int (*probe)(struct device *dev);//Device probe function void (*sync_state)(struct device *dev);//Device state synchronization function int (*remove)(struct device *dev);//Device remove function void (*shutdown)(struct device *dev); int (*online)(struct device *dev);//Device online function int (*offline)(struct device *dev);//Device offline function int (*suspend)(struct device *dev, pm_message_t state);//Device suspend function int (*resume)(struct device *dev);//Device resume function int (*num_vf)(struct device *dev);//Device virtual function count function int (*dma_configure)(struct device *dev);//Device DMA configuration function const struct dev_pm_ops *pm;// Device power management operations const struct iommu_ops *iommu_ops;// Device IOMMU operations struct subsys_private *p; // Subsystem private data struct lock_class_key lock_key; // Lock class key for lock mechanism bool need_parent_lock; // Whether parent lock is required}; |
struct device
The device structure is a data structure in the Linux kernel used to describe devices.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 | // include/linux/device.h/** * struct device - The basic device structure * @parent: The device's "parent" device, the device to which it is attached. * In most cases, a parent device is some sort of bus or host * controller. If parent is NULL, the device, is a top-level device, * which is not usually what you want. * @p: Holds the private data of the driver core portions of the device. * See the comment of the struct device_private for detail. * @kobj: A top-level, abstract class from which other classes are derived. * @init_name: Initial name of the device. * @type: The type of device. * This identifies the device type and carries type-specific * information. * @mutex: Mutex to synchronize calls to its driver. * @lockdep_mutex: An optional debug lock that a subsystem can use as a * peer lock to gain localized lockdep coverage of the device_lock. * @bus: Type of bus device is on. * @driver: Which driver has allocated this * @platform_data: Platform data specific to the device. * Example: For devices on custom boards, as typical of embedded * and SOC based hardware, Linux often uses platform_data to point * to board-specific structures describing devices and how they * are wired. That can include what ports are available, chip * variants, which GPIO pins act in what additional roles, and so * on. This shrinks the "Board Support Packages" (BSPs) and * minimizes board-specific #ifdefs in drivers. * @driver_data: Private pointer for driver specific info. * @links: Links to suppliers and consumers of this device. * @power: For device power management. * See Documentation/driver-api/pm/devices.rst for details. * @pm_domain: Provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions * along with subsystem-level and driver-level callbacks. * @em_pd: device's energy model performance domain * @pins: For device pin management. * See Documentation/driver-api/pinctl.rst for details. * @msi_list: Hosts MSI descriptors * @msi_domain: The generic MSI domain this device is using. * @numa_node: NUMA node this device is close to. * @dma_ops: DMA mapping operations for this device. * @dma_mask: Dma mask (if dma'ble device). * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all * hardware supports 64-bit addresses for consistent allocations * such descriptors. * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller * DMA limit than the device itself supports. * @dma_range_map: map for DMA memory ranges relative to that of RAM * @dma_parms: A low level driver may set these to teach IOMMU code about * segment limitations. * @dma_pools: Dma pools (if dma'ble device). * @dma_mem: Internal for coherent mem override. * @cma_area: Contiguous memory area for dma allocations * @archdata: For arch-specific additions. * @of_node: Associated device tree node. * @fwnode: Associated device node supplied by platform firmware. * @devt: For creating the sysfs "dev". * @id: device instance * @devres_lock: Spinlock to protect the resource of the device. * @devres_head: The resources list of the device. * @knode_class: The node used to add the device to the class list. * @class: The class of the device. * @groups: Optional attribute groups. * @release: Callback to free the device after all references have * gone away. This should be set by the allocator of the * device (i.e. the bus driver that discovered the device). * @iommu_group: IOMMU group the device belongs to. * @iommu: Per device generic IOMMU runtime data * @removable: Whether the device can be removed from the system. This * should be set by the subsystem / bus driver that discovered * the device. * * @offline_disabled: If set, the device is permanently online. * @offline: Set after successful invocation of bus type's .offline(). * @of_node_reused: Set if the device-tree node is shared with an ancestor * device. * @state_synced: The hardware state of this device has been synced to match * the software state of this device by calling the driver/bus * sync_state() callback. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the * streaming DMA operations (->map_* / ->unmap_* / ->sync_*), * and optionall (if the coherent mask is large enough) also * for dma allocations. This flag is managed by the dma ops * instance from ->dma_supported. * * At the lowest level, every device in a Linux system is represented by an * instance of struct device. The device structure contains the information * that the device model core needs to model the system. Most subsystems, * however, track additional information about the devices they host. As a * result, 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. */struct device { struct kobject kobj; //Corresponding kobj struct device *parent; // Device's parent device struct device_private *p; // Private pointer const char *init_name; /* initial name of the device */ //Device initialization name const struct device_type *type; struct bus_type *bus; /* type of bus device is on */ //Bus to which the device belongs struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */ void *driver_data; /* Driver data, set and get with dev_set_drvdata/dev_get_drvdata */ struct mutex lockdep_mutex; struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct dev_links_info links; struct dev_pm_info power; struct dev_pm_domain *pm_domain; struct em_perf_domain *em_pd; struct irq_domain *msi_domain; struct dev_pin_info *pins; raw_spinlock_t msi_lock; struct list_head msi_list; const struct dma_map_ops *dma_ops; u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ u64 bus_dma_limit; /* upstream dma constraint */ const struct bus_dma_region *dma_range_map; struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ struct cma *cma_area; /* contiguous memory area for dma allocations */ /* arch specific additions */ struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ struct fwnode_handle *fwnode; /* firmware device node */ int numa_node; /* NUMA node this device is close to */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ spinlock_t devres_lock; struct list_head devres_head; struct class *class; //Class to which the device belongs const struct attribute_group **groups; /* optional groups */ //Device attribute group void (*release)(struct device *dev); struct iommu_group *iommu_group; struct dev_iommu *iommu; enum device_removable removable; bool offline_disabled:1; bool offline:1; bool of_node_reused:1; bool state_synced:1; bool dma_coherent:1; bool dma_ops_bypass : 1;}; |
struct device_driver
struct device_driverIt is a data structure in the Linux kernel that describes device drivers.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 | // include/linux/device/driver.h/** * struct device_driver - The basic device driver structure * @name: Name of the device driver. * @bus: The bus which the device of this driver belongs to. * @owner: The module owner. * @mod_name: Used for built-in modules. * @suppress_bind_attrs: Disables bind/unbind via sysfs. * @probe_type: Type of the probe (synchronous or asynchronous) to use. * @of_match_table: The open firmware table. * @acpi_match_table: The ACPI match table. * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. * @sync_state: Called to sync device state to software state after all the * state tracking consumers linked to this device (present at * the time of late_initcall) have successfully bound to a * driver. If the device has no consumers, this function will * be called at late_initcall_sync level. If the device has * consumers that are never bound to a driver, this function * will never get called until they do. * @remove: Called when the device is removed from the system to * unbind a device from this driver. * @shutdown: Called at shut-down time to quiesce the device. * @suspend: Called to put the device to sleep mode. Usually to a * low power state. * @resume: Called to bring a device from sleep mode. * @groups: Default attributes that get created by the driver core * automatically. * @dev_groups: Additional attributes attached to device instance once the * it is bound to the driver. * @pm: Power management operations of the device which matched * this driver. * @coredump: Called when sysfs entry is written to. The device driver * is expected to call the dev_coredump API resulting in a * uevent. * @p: Driver core's private data, no one other than the driver * core can touch this. * * The device driver-model tracks all of the drivers known to the system. * The main reason for this tracking is to enable the driver core to match * up drivers with new devices. Once drivers are known objects within the * system, however, a number of other things become possible. Device drivers * can export information and configuration variables that are independent * of any specific device. */struct device_driver { const char *name;// Device driver name struct bus_type *bus; // Bus type to which the device driver belongs struct module *owner; // The module that owns the driver. const char *mod_name; /* used for built-in modules */ // Name used for built-in modules bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ //Disable the attribute for binding/unbinding via sysfs. enum probe_type probe_type; // Probe type, used to specify the probing method const struct of_device_id *of_match_table; // Device match table const struct acpi_device_id *acpi_match_table; // ACPI device match table // Note: device and device_driver must be attached to the same bus. Only then can probe be triggered. int (*probe) (struct device *dev);// Device probe function, used to initialize and configure the device void (*sync_state)(struct device *dev); // Device state synchronization function int (*remove) (struct device *dev); // Device remove function void (*shutdown) (struct device *dev); // Device shutdown function int (*suspend) (struct device *dev, pm_message_t state); // Device suspend function int (*resume) (struct device *dev); // Device resume function const struct attribute_group **groups; // Driver attribute group const struct attribute_group**dev_groups; const struct dev_pm_ops *pm; // Power management operations void (*coredump) (struct device *dev);//Device core dump function struct driver_private *p; //Driver's private data.}; |
struct class
struct classIt is a data structure in the Linux kernel that describes device classes.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 | // include/linux/device/class.h/** * struct class - device classes * @name: Name of the class. * @owner: The module owner. * @class_groups: Default attributes of this class. * @dev_groups: Default attributes of the devices that belong to the class. * @dev_kobj: The kobject that represents this class and links it into the hierarchy. * @dev_uevent: Called when a device is added, removed from this class, or a * few other things that generate uevents to add the environment * variables. * @devnode: Callback to provide the devtmpfs. * @class_release: Called to release this class. * @dev_release: Called to release the device. * @shutdown_pre: Called at shut-down time before driver shutdown. * @ns_type: Callbacks so sysfs can detemine namespaces. * @namespace: Namespace of the device belongs to this class. * @get_ownership: Allows class to specify uid/gid of the sysfs directories * for the devices belonging to the class. Usually tied to * device's namespace. * @pm: The default device power management operations of this class. * @p: The private data of the driver core, no one other than the * driver core can touch this. * * A class is a higher-level view of a device that abstracts out low-level * implementation details. Drivers may see a SCSI disk or an ATA disk, but, * at the class level, they are all simply disks. Classes allow user space * to work with devices based on what they do, rather than how they are * connected or how they work. */struct class { const char *name; //Name of the device class struct module *owner;//Module that owns the class // Class attribute group, used to describe the attributes of the device class. When the class is registered with the kernel, corresponding attribute files are automatically created under /sys/class/xxx_class. const struct attribute_group **class_groups; // Device attribute group, used to describe the attributes of the device. When the class is registered with the kernel, corresponding attribute files are automatically created in the device directory under the class. const struct attribute_group**dev_groups; // Kernel object of the device struct kobject *dev_kobj; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);// Event handler function of the device char *(*devnode)(struct device *dev, umode_t *mode);// Function for generating device nodes void (*class_release)(struct class *class);// Release function for class resources void (*dev_release)(struct device *dev);// Release function for device resources int (*shutdown_pre)(struct device *dev);// Callback function before device shutdown const struct kobj_ns_type_operations *ns_type;// Namespace type operations const void *(*namespace)(struct device *dev);// Namespace functions void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);// Function for acquiring device ownership const struct dev_pm_ops *pm;// Power management operations struct subsys_private *p;// Subsystem private data}; |
sysfs file system
The sysfs file system is a type ofvirtual file system,used to provide user space with information about devices, drivers, and other kernel objects in the kernel. It organizes data in a hierarchical manner and represents this data as files and directories, allowing user space to access and manipulate the attributes of kernel objects through the file system interface.
sysfs provides a unified interface for browsing and managing devices, buses, drivers, and other kernel objects in the kernel. It is/sysmounted under the directory, users can view and modify/sysfiles and directories under the directory to obtain and configure information about kernel objects.
We can regard buses, devices, and drivers as derived classes of kobject. Because they are all entities in the device model, they achieve integration with the device model by inheriting or extending kobject.

kobject is the cornerstone of the device model. By creating corresponding directory structures and attribute files, it provides a unified interface and framework for managing and operating various entities in the device model.
Source code analysis of file creation
Below, we explain layer by layer at the code level why when usingkobject_create_and_add()the function creates a kobject, if the parent node is NULL, it will be in the system root directory/syscreate under
The step-by-step path tracing is as follows:kobject_create_and_add
└──kobject_add
└──kobject_add_varg
└──kobject_add_internal
└──create_dir
└──sysfs_create_dir_ns(fs/sysfs/dir.c)
kobject_create_and_add()
123456789101112131415161718192021222324252627282930313233 | // lib/kobject.c/** * kobject_create_and_add() - Create a struct kobject dynamically and * register it with sysfs. * @name: the name for the kobject * @parent: the parent kobject of this kobject, if any. * * This function creates a kobject structure dynamically and registers it * with sysfs. When you are finished with this structure, call * kobject_put() and the structure will be dynamically freed when * it is no longer being used. * * If the kobject was not able to be created, NULL will be returned. */struct kobject *kobject_create_and_add(const char *name, struct kobject *parent){ struct kobject *kobj; int retval; kobj = kobject_create(); if (!kobj) return NULL; retval = kobject_add(kobj, parent, "%s", name); if (retval) { pr_warn("%s: kobject_add error: %d\n", __func__, retval); kobject_put(kobj); kobj = NULL; } return kobj;}EXPORT_SYMBOL_GPL(kobject_create_and_add); |
kobject_add()
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 | /** * kobject_add() - The main kobject add function. * @kobj: the kobject to add * @parent: pointer to the parent of the kobject. * @fmt: format to name the kobject with. * * The kobject name is set and added to the kobject hierarchy in this * function. * * If @parent is set, then the parent of the @kobj will be set to it. * If @parent is NULL, then the parent of the @kobj will be set to the * kobject associated with the kset assigned to this kobject. If no kset * is assigned to the kobject, then the kobject will be located in the * root of the sysfs tree. * * Note, no "add" uevent will be created with this call, the caller should set * up all of the necessary sysfs files for the object and then call * kobject_uevent() with the UEVENT_ADD parameter to ensure that * userspace is properly notified of this kobject's creation. * * Return: If this function returns an error, kobject_put() must be * called to properly clean up the memory associated with the * object. Under no instance should the kobject that is passed * to this function be directly freed with a call to kfree(), * that can leak memory. * * If this function returns success, kobject_put() must also be called * in order to properly clean up the memory associated with the object. * * In short, once this function is called, kobject_put() MUST be called * when the use of the object is finished in order to properly free * everything. */int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...){ va_list args; int retval; if (!kobj) return -EINVAL; if (!kobj->state_initialized) { pr_err("kobject '%s' (%p): tried to add an uninitialized object, something is seriously wrong.\n", kobject_name(kobj), kobj); dump_stack(); return -EINVAL; } va_start(args, fmt); retval = kobject_add_varg(kobj, parent, fmt, args); va_end(args); return retval;}EXPORT_SYMBOL(kobject_add); |
kobject_add_varg()
1234567891011121314 | static __printf(3, 0) int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs){ int retval; retval = kobject_set_name_vargs(kobj, fmt, vargs); if (retval) { pr_err("kobject: can not set name properly!\n"); return retval; } kobj->parent = parent; return kobject_add_internal(kobj);} |
kobject_add_internal()
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 | static int kobject_add_internal(struct kobject *kobj){ int error = 0; struct kobject *parent; if (!kobj) return -ENOENT; if (!kobj->name || !kobj->name[0]) { WARN(1, "kobject: (%p): attempted to be registered with empty name!\n", kobj); return -EINVAL; } parent = kobject_get(kobj->parent); /* join kset if set, use it as parent if we do not already have one */ if (kobj->kset) { if (!parent) parent = kobject_get(&kobj->kset->kobj); kobj_kset_join(kobj); kobj->parent = parent; } pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n", kobject_name(kobj), kobj, __func__, parent ? kobject_name(parent) : "<NULL>", kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); error = create_dir(kobj); if (error) { kobj_kset_leave(kobj); kobject_put(parent); kobj->parent = NULL; /* be noisy on error issues */ if (error == -EEXIST) pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n", __func__, kobject_name(kobj)); else pr_err("%s failed for %s (error: %d parent: %s)\n", __func__, kobject_name(kobj), error, parent ? kobject_name(parent) : "'none'"); } else kobj->state_in_sysfs = 1; return error;} |
create_dir()
123456789101112131415161718192021222324252627282930313233343536373839404142434445 | static int create_dir(struct kobject *kobj){ const struct kobj_type *ktype = get_ktype(kobj); const struct kobj_ns_type_operations *ops; int error; error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); if (error) return error; error = populate_dir(kobj); if (error) { sysfs_remove_dir(kobj); return error; } if (ktype) { error = sysfs_create_groups(kobj, ktype->default_groups); if (error) { sysfs_remove_dir(kobj); return error; } } /* * @kobj->sd may be deleted by an ancestor going away. Hold an * extra reference so that it stays until @kobj is gone. */ sysfs_get(kobj->sd); /* * If @kobj has ns_ops, its children need to be filtered based on * their namespace tags. Enable namespace support on @kobj->sd. */ ops = kobj_child_ns_ops(kobj); if (ops) { BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE); BUG_ON(ops->type >= KOBJ_NS_TYPES); BUG_ON(!kobj_ns_type_registered(ops->type)); sysfs_enable_ns(kobj->sd); } return 0;} |
It can be seen thaterror = create_dir(kobj);Create a folder in the file system
sysfs_create_dir_ns()
sysfs_create_dir_nsThe function is as follows
12345678910111213141516171819202122232425262728293031323334353637 | // fs/sysfs/dir.c/** * sysfs_create_dir_ns - create a directory for an object with a namespace tag * @kobj: object we're creating directory for * @ns: the namespace tag to use */int sysfs_create_dir_ns(struct kobject *kobj, const void *ns){ struct kernfs_node *parent, *kn; kuid_t uid; kgid_t gid; if (WARN_ON(!kobj)) return -EINVAL; if (kobj->parent) parent = kobj->parent->sd; else parent = sysfs_root_kn; if (!parent) return -ENOENT; kobject_get_ownership(kobj, &uid, &gid); kn = kernfs_create_dir_ns(parent, kobject_name(kobj), S_IRWXU | S_IRUGO | S_IXUGO, uid, gid, kobj, ns); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(parent, kobject_name(kobj)); return PTR_ERR(kn); } kobj->sd = kn; return 0;} |
In the above function, when there is no parent node, the parent node is assigned tosysfs_root_kn, i.e./systhe node of the root directory. If there isparent, then its parent node iskobj->parent->sd, then callkernfs_create_dir_nsto create the directory.
Andsysfs_root_knDuring sysfs file system initialization, i.e.,sysfs_initis created in the function:
123456789101112131415161718192021222324252627 | // fs/sysfs/mount.cstatic struct file_system_type sysfs_fs_type = { .name = "sysfs", .init_fs_context = sysfs_init_fs_context, .kill_sb = sysfs_kill_sb, .fs_flags = FS_USERNS_MOUNT,};int __init sysfs_init(void){ int err; sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK, NULL); if (IS_ERR(sysfs_root)) return PTR_ERR(sysfs_root); sysfs_root_kn = sysfs_root->kn; err = register_filesystem(&sysfs_fs_type); if (err) { kernfs_destroy_root(sysfs_root); return err; } return 0;} |
Through the above analysis of the API functions, we can summarize the rules for creating directories, as follows:
- No parent directory, no kset: it will be in the root directory of sysfs (i.e.,
/sys) create the directory under it. - No parent directory, but with kset: create the directory under the kset, and add the kobj to
kset.list。 - With parent directory, no kset: create the directory under parent.
- With parent directory and kset: create the directory under parent, and add the kobj to
kset.list。
Analysis of sysfs directory hierarchy
The folders related to the device model are bus, class, and devices. The full paths are as follows:
/sys/bus/sys/class/sys/devices
The explanation is as follows:
/sys/devices
This directory contains subdirectories for all devices in the system.**Each device subdirectory represents a specific device.**It reflects the relationships and topology of devices through its path hierarchy and symbolic links. Each device subdirectory contains the device’s attributes, status, and other related information.

/sys/bus
This directory contains subdirectories for bus types. Each subdirectory represents a specific type of bus, such as PCI, USB, etc. Each bus subdirectory contains information about the devices and drivers associated with that bus.

For example, devices connected under the I2C bus are shown below:

/sys/class
This directory contains subdirectories for device classes. Each subdirectory represents a device class, such as disks, network interfaces, etc. Each device class subdirectory contains information about the devices belonging to that class. As shown in the figure below

The benefits of classifying using class are as follows:
- Logical organization: By categorizing devices according to class, a logical organizational structure can be established in the device model. In this way,Devices of related types can be placed under the same class directoryThis makes the organizational structure of devices clearer and more manageable.
- Unified interfaces and attributes: Eachdevice class directory can define a set of unified interfaces and attributesIt is used to describe and configure the common characteristics and behaviors of all devices under that category. In this way, for devices of the same category, the same methods and attributes can be used to operate and configure them, simplifying the writing and maintenance of device drivers.
- Simplified device discovery and management: By classifying devices, a simplified device discovery and management mechanism can be provided. Users and applications can find and identify specific types of devices in the class directory without traversing the entire device model. In this way, device discovery and access become more efficient and convenient.
- Extensibility and portability: Use
class: Classification provides a mechanism for extensibility and portability. When a new device type is introduced, it can be classified into an existing category without modifying existing device management and drivers. This extensibility and portability makes the system more flexible, and makes it easier for developers and device suppliers to integrate new devices.
For example, if the application now needs to set GPIO, if using class, you can directly use the following command:
1 | echo 1 > /sys/class/gpio/gpio157/value |
If not using class, use the following command:
1 | echo 1 > /sys/devices/platform/fe770000.gpio/gpiochip4/gpio/gpio157/value |

Common sysfs APIs
kobject_init_and_add()
| Item | Description |
|---|---|
| Function definition | int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...); |
| Header file | #include <linux/kobject.h> |
| parameter kobj | kobject with allocated memory |
| parameter ktype | kobject type (attributes + callbacks) |
| Parameter parent | parent directory |
| Parameter fmt | sysfs directory name |
| Function | Initialize and register kobject |
| Effect | Create directory in sysfs |
| Return value | Success: 0 |
kobject_create_and_add()
| Item | Description |
|---|---|
| Function definition | struct kobject *kobject_create_and_add(const char *name, struct kobject *parent); |
| Function | Allocate + initialize + register |
| Applicable scenarios | Simple kobject |
| Return value | kobject pointer |
kobject_put()
| Item | Description |
|---|---|
| Function definition | void kobject_put(struct kobject *kobj); |
| Function | decrement reference count |
| Trigger | Count reaches 0 → release |
| Must | Yes |
kobj_type
struct kobj_type
| Member | Description |
|---|---|
| release | Release function (required) |
| sysfs_ops | sysfs read/write callbacks |
| default_attrs | Default attribute array |
release()
| Item | Description |
|---|---|
| Prototype | void (*release)(struct kobject *kobj); |
| Function | Free memory |
| Must | Must implement |
struct sysfs_ops
| Member | Description |
|---|---|
| show | Unified read callback |
| store | Unified write callback |
default_attrs
| Item | Description |
|---|---|
| Position | kobj_type |
| Type | struct attribute ** |
| Function | Automatically create attributes |
| Requirements | NULL-terminated |
Example:
12345 | struct attribute *attrs[] = { &attr1.attr, &attr2.attr, NULL}; |
Attribute-related
struct attribute
| Member | Description |
|---|---|
| name | Attribute file name |
| mode | Permissions (0644 / 0664) |
struct kobj_attribute
| Member | Description |
|---|---|
| attr | struct attribute |
| show | Read function |
| store | Write function |
__ATTR macro
| Item | Description |
|---|---|
| Macro definition | __ATTR(name, mode, show, store) |
| Function | Define attribute object |
| Generate type | struct kobj_attribute |
sysfs_create_file()
| Item | Description |
|---|---|
| Function definition | int sysfs_create_file(struct kobject *kobj, const struct attribute *attr); |
| Function | Manually add attribute file |
| Applicable | kobject_create_and_add scenario |
| Return value | 0 success |
sysfs_remove_file()
| Item | Description |
|---|---|
| Function definition | void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr); |
| Function | Delete attribute file |
sysfs_create_group()
| Item | Description |
|---|---|
| Function definition | int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); |
| Function | Create attribute group (directory + multiple files) |
| Return value | 0 success |
sysfs_remove_group()
| Item | Description |
|---|---|
| Function definition | void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp); |
| Function | Delete attribute group |
| Must | Paired with create |
struct attribute_group
| Member | Description |
|---|---|
| name | Subdirectory name (NULL → no directory created) |
| attrs | Attribute array |
| is_visible | Dynamic visibility control |
syfs attribute creation example
usekobject_init_and_add()
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 | // Custom structstruct mykobj { struct kobject kobj; // Embed kobject into a custom struct int value1; int value2;};static struct mykobj *mykobjp;void mykobj_release(struct kobject *kobj){ struct mykobj *p = container_of(kobj, struct mykobj, kobj); pr_info("mykobj (%p) free: %s\n", p, __func__); kfree(p);}struct attribute myattr1 = { .name = "myattr1", .mode = 0666,};struct attribute myattr2 = { .name = "myattr2", .mode = 0666,};struct attribute *mykobj_default_attrs[] = { &myattr1, &myattr2, NULL,};ssize_t myshow(struct kobject *kobj, struct attribute *attr, char *buf){ ssize_t count; struct mykobj *mykobj = container_of(kobj, struct mykobj, kobj); if (strcmp(attr->name, "myattr1") == 0) { count = sprintf(buf, "%d\n", mykobj->value1); } else if (strcmp(attr->name, "myattr2") == 0) { count = sprintf(buf, "%d\n", mykobj->value2); } else { count = 0; } return count;}ssize_t mystore(struct kobject *kobj, struct attribute *attr, const char *buf, size_t size){ struct mykobj *mykobj = container_of(kobj, struct mykobj, kobj); if (strcmp(attr->name, "myattr1") == 0) { sscanf(buf, "%d\n", &mykobj->value1); } else if (strcmp(attr->name, "myattr2") == 0) { sscanf(buf, "%d\n", &mykobj->value2); } return size;}const struct sysfs_ops my_sysfs_ops = { .show = myshow, .store = mystore,};struct kobj_type mytype = { .release = mykobj_release, .default_attrs = mykobj_default_attrs, .sysfs_ops = &my_sysfs_ops,};static int __init sysfs_attribute_test_init(void){ int ret; mykobjp = kzalloc(sizeof(struct mykobj), GFP_KERNEL); if (!mykobjp) return -ENOMEM; ret = kobject_init_and_add(&mykobjp->kobj, &mytype, NULL, "mykobject"); return 0;}static void __exit sysfs_attribute_test_exit(void){ kobject_put(&mykobjp->kobj);}module_init(sysfs_attribute_test_init);module_exit(sysfs_attribute_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a sample for sysfs attribute"); |
Test:
123456789101112131415161718192021222324252627 | $ insmod sysfs_attribute_test.ko[ 33.038129] sysfs_attribute_test: loading out-of-tree module taints kernel.$ cd /sys/sys $ lsblock class devices fs module powerbus dev firmware kernel mykobject/sys $ cd mykobject//sys/mykobject $ lsmyattr1 myattr2/sys/mykobject $ cat myattr10/sys/mykobject $ echo 3 > myattr1/sys/mykobject $ cat myattr13/sys/mykobject $ echo 4 > myattr2/sys/mykobject $ cat myattr24/sys/mykobject $ lsmodsysfs_attribute_test 16384 0 - Live 0xffffffc008b30000 (O)/sys/mykobject $ cd ..//sys $ rmmod sysfs_attribute_test.ko[ 108.441057] mykobj ((____ptrval____)) free: mykobj_release/sys $ cd /sys $ lsblock class devices fs modulebus dev firmware kernel power |
Can optimize attribute file read/write, each attribute corresponds to a read/write function, usekobj_attributeWrapper
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 | // Custom structstruct mykobj { struct kobject kobj; // Embed kobject into a custom struct}; static struct mykobj *mykobjp;void mykobj_release(struct kobject *kobj){ struct mykobj *p = container_of(kobj, struct mykobj, kobj); pr_info("mykobj (%p) free: %s\n", p, __func__); kfree(p);}// Define attribute objects myattr1 and myattr2struct myattribute{ struct kobj_attribute kobj_attr; int value;};// Custom show function for reading attribute valuesssize_t myattr1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ ssize_t count; struct myattribute *myattr = container_of(attr, struct myattribute, kobj_attr); count = sprintf(buf, "%d\n", myattr->value); return count;}// Custom store function for writing attribute valuesssize_t myattr1_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t size){ struct myattribute *myattr = container_of(attr, struct myattribute, kobj_attr); sscanf(buf, "%d\n", &myattr->value); return size;}// Custom show function for reading attribute valuesssize_t myattr2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ ssize_t count; struct myattribute *myattr = container_of(attr, struct myattribute, kobj_attr); count = sprintf(buf, "%d\n", myattr->value); return count;}// Custom store function for writing attribute valuesssize_t myattr2_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t size){ struct myattribute *myattr = container_of(attr, struct myattribute, kobj_attr); sscanf(buf, "%d\n", &myattr->value); return size;}static struct myattribute myattr1 = { .kobj_attr = __ATTR(myattr1, 0664, myattr1_show, myattr1_store), .value = 0,};static struct myattribute myattr2 = { .kobj_attr = __ATTR(myattr2, 0664, myattr2_show, myattr2_store), .value = 0,};struct attribute *mykobj_default_attrs[] = { &myattr1.kobj_attr.attr, &myattr2.kobj_attr.attr, NULL,};ssize_t myshow(struct kobject *kobj, struct attribute *attr, char *buf){ struct kobj_attribute *kobj_attr = container_of(attr, struct kobj_attribute, attr); return kobj_attr->show(kobj, kobj_attr, buf);}ssize_t mystore(struct kobject *kobj, struct attribute *attr, const char *buf, size_t size){ struct kobj_attribute *kobj_attr = container_of(attr, struct kobj_attribute, attr); return kobj_attr->store(kobj, kobj_attr, buf, size);}const struct sysfs_ops my_sysfs_ops = { .show = myshow, .store = mystore,};struct kobj_type mytype = { .release = mykobj_release, .default_attrs = mykobj_default_attrs, .sysfs_ops = &my_sysfs_ops,};static int __init sysfs_attribute_test_init(void){ int ret; mykobjp = kzalloc(sizeof(struct mykobj), GFP_KERNEL); if (!mykobjp) return -ENOMEM; ret = kobject_init_and_add(&mykobjp->kobj, &mytype, NULL, "mykobject"); return 0;}static void __exit sysfs_attribute_test_exit(void){ kobject_put(&mykobjp->kobj);}module_init(sysfs_attribute_test_init);module_exit(sysfs_attribute_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a sample for sysfs attribute"); |
Test:
123456789101112131415161718 | $ insmod sysfs_attribute_improved_test.ko[ 115.098529] sysfs_attribute_improved_test: loading out-of-tree module taints kernel.$ cd /sys/mykobject//sys/mykobject $ lsmyattr1 myattr2/sys/mykobject $ cat myattr10/sys/mykobject $ echo 1 > myattr1/sys/mykobject $ cat myattr11/sys/mykobject $ cd /sys/sys $ rmmod sysfs_attribute_improved_test.ko[ 149.750265] mykobj ((____ptrval____)) free: mykobj_release/sys $ lsblock class devices fs modulebus dev firmware kernel power |
usekobject_create_and_add()
usekobject_create_and_add(), need to callsysfs_create_fileAdd attribute file
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 | static int value1;static int value2;// Custom show function for reading attribute valuesssize_t myattr1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d\n", value1);}// Custom store function for writing attribute valuesssize_t myattr1_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t size){ sscanf(buf, "%d\n", &value1); return size;}// Custom show function for reading attribute valuesssize_t myattr2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d\n", value2);}// Custom store function for writing attribute valuesssize_t myattr2_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t size){ sscanf(buf, "%d\n", &value2); return size;}static struct kobj_attribute kobj_attr1 = __ATTR(myattr1, 0664, myattr1_show, myattr1_store);static struct kobj_attribute kobj_attr2 = __ATTR(myattr2, 0664, myattr2_show, myattr2_store);static struct kobject *mykobj;static int __init sysfs_attribute_test_init(void){ int ret; mykobj = kobject_create_and_add("mykobject", NULL); ret = sysfs_create_file(mykobj, &kobj_attr1.attr); ret = sysfs_create_file(mykobj, &kobj_attr2.attr); return 0;}static void __exit sysfs_attribute_test_exit(void){ kobject_put(mykobj);}module_init(sysfs_attribute_test_init);module_exit(sysfs_attribute_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a sample for sysfs attribute"); |
Test:
123456789101112131415 | $ insmod sysfs_attribute_another_way.ko[ 14.955347] sysfs_attribute_another_way: loading out-of-tree module taints kernel.$ cd /sys/mykobject//sys/mykobject $ lsmyattr1 myattr2/sys/mykobject $ cat myattr10/sys/mykobject $ echo 122 > myattr1/sys/mykobject $ cat myattr1122/sys/mykobject $ cd /sys/sys $ rmmod sysfs_attribute_another_way.ko/sys $ lsblock class devices fs modulebus dev firmware kernel power |
sysfs_create_group()Create multiple attribute files
sysfs_create_group()Used to create an attribute group (directory + multiple attribute files) for a kobject in sysfs.
Function prototype
1 | int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); |
Parameter description
1.kobj
- Points to a
struct kobject - The attribute group will be a subdirectory under the kobject’s directory (if the group name exists) or a same-level attribute (when the group name is NULL).
2.grp
Points to astruct attribute_group, used to describe the group name and the list of attribute files.
struct attribute_group structure
1234567 | struct attribute_group { const char *name; const struct attribute **attrs; mode_t (*is_visible)(struct kobject *kobj, struct attribute *attr, int index);}; |
Field description:
| Member | Description |
|---|---|
name | Group name. If not NULL, a directory will be created under sysfs. |
attrs | Attribute array, terminated by NULL; each entry is astruct attribute *。 |
is_visible | (Optional) Determines whether the attribute is visible based on index; if not visible, the attribute file will not be created. |
example
- Define attribute file
12345 | static struct kobj_attribute attr1 = __ATTR(attr1, 0644, attr1_show, attr1_store);static struct kobj_attribute attr2 = __ATTR(attr2, 0644, attr2_show, attr2_store); |
- Create attribute array (NULL-terminated)
12345 | struct attribute *attrs[] = { &attr1.attr, &attr2.attr, NULL,}; |
- Define attribute group
1234 | const struct attribute_group attr_group = { .name = "my_group", .attrs = attrs,}; |
- Register attribute group
1 | sysfs_create_group(kobj, &attr_group); |
- Remove attribute group (must be used in pairs)
Need to remove it when the module exits or the device is removed:
1 | sysfs_remove_group(kobj, &attr_group); |
Driver:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 | static int value1;static int value2;// Custom show function for reading attribute valuesssize_t myattr1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d\n", value1);}// Custom store function for writing attribute valuesssize_t myattr1_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t size){ sscanf(buf, "%d\n", &value1); return size;}// Custom show function for reading attribute valuesssize_t myattr2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d\n", value2);}// Custom store function for writing attribute valuesssize_t myattr2_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t size){ sscanf(buf, "%d\n", &value2); return size;}static struct kobj_attribute kobj_attr1 = __ATTR(myattr1, 0664, myattr1_show, myattr1_store);static struct kobj_attribute kobj_attr2 = __ATTR(myattr2, 0664, myattr2_show, myattr2_store);struct attribute *attr_array[] = { &kobj_attr1.attr, &kobj_attr2.attr, NULL,};const struct attribute_group attr_grp = { .name = "mygroup", .attrs = attr_array,};static struct kobject *mykobj;static int __init sysfs_attribute_test_init(void){ int ret; mykobj = kobject_create_and_add("mykobject", NULL); ret = sysfs_create_group(mykobj, &attr_grp); return ret;}static void __exit sysfs_attribute_test_exit(void){ sysfs_remove_group(mykobj, &attr_grp); kobject_put(mykobj);}module_init(sysfs_attribute_test_init);module_exit(sysfs_attribute_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a sample for sysfs attribute"); |
Test:
12345678910111213141516171819202122 | $ insmod sysfs_attr_group_test.ko[ 13.889636] sysfs_attr_group_test: loading out-of-tree module taints kernel.$ cd /sys//sys $ lsblock class devices fs module powerbus dev firmware kernel mykobject/sys $ cd mykobject//sys/mykobject $ lsmygroup/sys/mykobject $ cd mygroup//sys/mykobject/mygroup $ lsmyattr1 myattr2/sys/mykobject/mygroup $ cat myattr20/sys/mykobject/mygroup $ echo 2 > myattr2/sys/mykobject/mygroup $ cat myattr22/sys/mykobject/mygroup $ cd /sys/sys $ rmmod sysfs_attr_group_test.ko/sys $ lsblock class devices fs modulebus dev firmware kernel power |
More references:
In sysfs, newer kernel versions recommend using sys_emit for printing, while older versions use scnprintf
Register bus
bus_register()
| Item | Description |
|---|---|
| Function definition | int bus_register(struct bus_type *bus); |
| Header file | #include <linux/device/bus.h>or#include <linux/device.h> |
| Parameter bus | point tostruct bus_typepointer to the custom bus to register |
| Function | Registers a custom bus with the Linux kernel, enabling the kernel to recognize the bus and provide device-driver matching mechanism |
| Return value | Success: returns 0; On failure: returns a negative error code (e.g. -EINVAL,-ENOMEMetc.) |
bus_unregister()
| Item | Description |
|---|---|
| Function definition | void bus_unregister(struct bus_type *bus); |
| Header file | #include <linux/device/bus.h>or#include <linux/device.h> |
| Parameter bus | point tostruct bus_typepointer to the custom bus to unregister |
| Function | Unregisters a previously registered custom bus, releases related resources, and makes it no longer visible in the kernel |
| Return value | No return value |
example
123456789101112131415161718192021222324252627282930313233343536373839404142 | int mybus_match(struct device *dev, struct device_driver *drv){ return (strcmp(dev_name(dev), drv->name) == 0);}int mybus_probe(struct device *dev){ struct device_driver *drv = dev->driver; if (drv->probe) drv->probe(dev); return 0;}struct bus_type mybus_type = { .name = "mybus", .match = mybus_match, .probe = mybus_probe,};static int __init my_own_bus_test_init(void){ int ret; ret = bus_register(&mybus_type); return ret;}static void __exit my_own_bus_test_exit(void){ bus_unregister(&mybus_type);}module_init(my_own_bus_test_init);module_exit(my_own_bus_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a test sample for bus"); |
Test:
12345678910111213141516 | $ insmod bus_register_test.ko[ 19.436050] bus_register_test: loading out-of-tree module taints kernel.$ ls /sys/bus/amba gpio mmc_rpmb serialclockevents hid mybus socclocksource i2c nvmem spicontainer iscsi_flashnode pci usbcpu mdio_bus platform workqueueevent_source mipi-dsi scsigenpd mmc sdio$ ls /sys/bus/mybusdevices drivers_autoprobe ueventdrivers drivers_probe$ rmmod bus_register_test.ko$ ls /sys/bus/mybusls: /sys/bus/mybus: No such file or directory |
Create attribute under bus directory
bus_create_file()
| Item | Description |
|---|---|
| Function definition | int bus_create_file(struct bus_type *bus, struct bus_attribute *bus_attr); |
| Header file | #include <linux/device/bus.h>or#include <linux/device.h> |
| Parameter bus | point tostruct bus_typepointer to the bus on which to create the attribute file |
| Parameter bus_attr | point tostruct bus_attributepointer to the attribute structure describing the attribute file to create (including name, permissions, show/store) |
| Function | Creates an attribute file in the sysfs directory corresponding to the bus (e.g./sys/bus/<busname>/value) |
| Return value | Success: returns 0; On failure: negative error code |
Attribute structure (struct bus_attribute) example description table
| field | Description |
|---|---|
| attr.name | Attribute file name |
| attr.mode | File permissions, e.g.0664 |
| show | sysfs read callback (for cat reading) |
| store | sysfs write callback (for echo writing) |
Example usage:
123456789 | struct bus_attribute mybus_attr = { .attr = { .name = "value", .mode = 0664, }, .show = mybus_show,};ret = bus_create_file(&mybus, &mybus_attr); |
Example code
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 | int mybus_match(struct device *dev, struct device_driver *drv){ // Check whether the device name and driver name match return (strcmp(dev_name(dev), drv->name) == 0);};int mybus_probe(struct device *dev){ struct device_driver *drv = dev->driver; if (drv->probe) drv->probe(dev); return 0;};struct bus_type mybus = { .name = "mybus", // Bus name .match = mybus_match, // Device and driver matching callback function .probe = mybus_probe, // Device probe callback function};EXPORT_SYMBOL_GPL(mybus); // Export bus symbolsssize_t mybus_show(struct bus_type *bus, char *buf){ // Show bus value in sysfs return sprintf(buf, "%s\n", "mybus_show");};struct bus_attribute mybus_attr = { .attr = { .name = "value", // Attribute name .mode = 0664, // Attribute access permissions }, .show = mybus_show, // Attribute show callback function};// Module initialization functionstatic int bus_init(void){ int ret; ret = bus_register(&mybus); // Register bus ret = bus_create_file(&mybus, &mybus_attr); // Create attribute file in sysfs return 0;}// Module exit functionstatic void bus_exit(void){ bus_remove_file(&mybus, &mybus_attr); // Remove attribute file from sysfs bus_unregister(&mybus); // Unregister bus}module_init(bus_init); // Specify the module's initialization functionmodule_exit(bus_exit); // Specify the module's exit functionMODULE_LICENSE("GPL"); // The license used by the moduleMODULE_AUTHOR("topeet"); // The module's author |
Test:
123456789101112 | $ insmod create_attr_under_my_own_bus.ko[ 23.827916] create_attr_under_my_own_bus: loading out-of-tree module taints kernel.$ cd /sys/bus/mybus//sys/bus/mybus $ lsdevices drivers_autoprobe ueventdrivers drivers_probe value/sys/bus/mybus $ cat valuemybus_show/sys/bus/mybus $ cd /$ rmmod create_attr_under_my_own_bus.ko$ ls /sys/bus/mybusls: /sys/bus/mybus: No such file or directory |
Bus registration process analysis
bus_register()
struct bus_typeStructure:
12345678910111213141516171819202122232425262728293031323334353637 | // include/linux/device/bus.hstruct bus_type { const char *name; const char *dev_name; struct device *dev_root;// device structure, dev_root const struct attribute_group **bus_groups; const struct attribute_group**dev_groups; const struct attribute_group **drv_groups; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); void (*sync_state)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*online)(struct device *dev); int (*offline)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); int (*num_vf)(struct device *dev); int (*dma_configure)(struct device *dev); const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; struct subsys_private *p; struct lock_class_key lock_key; bool need_parent_lock;}; |
It can be seen thatstruct bus_typeThe structure containsstruct devicestructure, while the device structure contains kobject
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 | // include/linux/device.hstruct 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; 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, device core doesn't touch it */ void *driver_data; /* Driver data, set and get with dev_set_drvdata/dev_get_drvdata */ struct mutex lockdep_mutex; struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct dev_links_info links; struct dev_pm_info power; struct dev_pm_domain *pm_domain; struct em_perf_domain *em_pd; struct irq_domain *msi_domain; struct dev_pin_info *pins; raw_spinlock_t msi_lock; struct list_head msi_list; const struct dma_map_ops *dma_ops; u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ u64 bus_dma_limit; /* upstream dma constraint */ const struct bus_dma_region *dma_range_map; struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ struct cma *cma_area; /* contiguous memory area for dma allocations */ /* arch specific additions */ struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ struct fwnode_handle *fwnode; /* firmware device node */ int numa_node; /* NUMA node this device is close to */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ spinlock_t devres_lock; struct list_head devres_head; struct class *class; const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); struct iommu_group *iommu_group; struct dev_iommu *iommu; enum device_removable removable; bool offline_disabled:1; bool offline:1; bool of_node_reused:1; bool state_synced:1; bool dma_coherent:1; bool dma_ops_bypass : 1;}; |
Andbus_register()in
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 | /** * bus_register - register a driver-core subsystem * @bus: bus to register * * Once we have that, we register the bus with the kobject * infrastructure, then register the children subsystems it has: * the devices and drivers that belong to the subsystem. */int bus_register(struct bus_type *bus){ int retval; struct subsys_private *priv; struct lock_class_key *key = &bus->lock_key; // Allocate and initialize a subsys_private structure to store subsystem-related information priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->bus = bus; bus->p = priv; // Initialize a blocking notifier chain bus_notifier BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); // Set the subsystem name retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; // Set the subsystem's kset and ktype priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; // kset for registering the subsystem retval = kset_register(&priv->subsys); if (retval) goto out; // Create an attribute file on the bus retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; // Create and add the kset for the "devices" subdirectory priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } // Create and add the kset for the "drivers" subdirectory priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } // Initialize the interface list, mutex, and device/driver klist INIT_LIST_HEAD(&priv->interfaces); __mutex_init(&priv->mutex, "subsys mutex", key); klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); // Add the driver probe file retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; // Add the bus's attribute group retval = bus_add_groups(bus, bus->bus_groups); if (retval) goto bus_groups_fail; pr_debug("bus: '%s': registered\n", bus->name); return 0;bus_groups_fail: remove_probe_files(bus);bus_probe_files_fail: kset_unregister(bus->p->drivers_kset);bus_drivers_fail: kset_unregister(bus->p->devices_kset);bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent);bus_uevent_fail: kset_unregister(&bus->p->subsys); /* Above kset_unregister() will kfree @bus->p */ bus->p = NULL;out: kfree(bus->p); bus->p = NULL; return retval;}EXPORT_SYMBOL_GPL(bus_register); |
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
This line of code initializes the namedpriv->klist_deviceskernel linked list.klist_devices_getandklist_devices_putare two callback functions, used
to perform corresponding operations when adding or removing elements from the linked list.
Usually, these callback functionsare used to perform additional operations when each element in the linked list is referenced or released. For example, when a device is added to the linked list,klist_devices_getthe function may increase the device’s reference count; when the device is removed from the linked list,klist_devices_putthe function may decrease the device’s reference count.
klist_init(&priv->klist_drivers, NULL, NULL);
This line of code initializes the namedpriv->klist_driverskernel linked list, but unlike the first initialization, no callback functions are provided here. Therefore, this linked list does not perform additional operations when adding or removing elements. In this case, the linked list is mainly used to store driver objects without additional processing logic.
subsys_private()
A new structure subsys_private appears:
1234567891011121314151617181920212223242526272829303132333435363738394041424344 | // drivers/base/base.h/** * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure. * * @subsys - the struct kset that defines this subsystem * @devices_kset - the subsystem's 'devices' directory * @interfaces - list of subsystem interfaces associated * @mutex - protect the devices, and interfaces lists. * * @drivers_kset - the list of drivers associated * @klist_devices - the klist to iterate over the @devices_kset * @klist_drivers - the klist to iterate over the @drivers_kset * @bus_notifier - the bus notifier list for anything that cares about things * on this bus. * @bus - pointer back to the struct bus_type that this structure is associated * with. * * @glue_dirs - "glue" directory to put in-between the parent device to * avoid namespace conflicts * @class - pointer back to the struct class that this structure is associated * with. * * This structure is the one that is the actual kobject allowing struct * bus_type/class to be statically allocated safely. Nothing outside of the * driver core should ever touch these fields. */struct subsys_private { struct kset subsys; struct kset *devices_kset; struct list_head interfaces; struct mutex mutex; struct kset *drivers_kset; struct klist klist_devices; struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; struct bus_type *bus; struct kset glue_dirs; struct class *class;}; |
struct subsys_privateis a structure used to store private information of the driver core subsystem (bus). Each subsystem can have private data, and this private data is stored instruct subsys_privatethe structure.
Linux subsystem
In Linux,a subsystem is a mechanism for abstracting the implementation of a specific function into an independent entity. It provides a convenient way to organize related code and data structures together to implement specific functions. A subsystem can be viewed as a functional module that encapsulates related functions and operations, allowing users and applications to interact with it through a unified interface.
In Linux, there are many common subsystems, each responsible for implementing specific functions. The following are some examples of common subsystems.
- Virtual File System (VFS) subsystem: The VFS subsystem provides a unified access interface to different file systems, allowing applications to transparently access various file systems (such as ext4, NTFS, FAT, etc.) without needing to care about the specific implementation of the underlying file system.
- Device driver subsystem: The device driver subsystem manages and controls the drivers of hardware devices. It provides an interface for interacting with hardware devices, allowing applications to communicate with and control devices through drivers.
- network subsystem: The network subsystem is responsible for managing and controlling network-related functions. It includes the network protocol stack, socket interfaces, network device drivers, etc., for implementing network communication and processing network protocols.
- Memory management subsystem: The memory management subsystem is responsible for managing the system’s physical and virtual memory. It includes functions such as memory allocation, page replacement, and memory mapping, used to efficiently allocate and manage the system’s memory resources.
- Process management subsystem: The process management subsystem is responsible for managing and controlling processes in the system. It includes functions such as process creation, scheduling, and termination, as well as inter-process communication mechanisms such as signals, pipes, and shared memory.
- Power management subsystem: The power management subsystem is responsible for managing and controlling the system’s power management functions. It can be used to control power on/off, switch power modes, and implement energy-saving features.
- File system subsystem: The file system subsystem is responsible for managing and controlling operations such as file system creation, formatting, mounting, and data access. It supports various file system types, such as ext4, FAT, NTFS, etc.
- Graphics subsystem: The graphics subsystem is responsible for managing and controlling graphics display functions, including display drivers, window management, and graphics rendering. It provides support for graphical interfaces, allowing users to interact with the computer in a graphical way.
Summary
- kobject and kset are the basic framework of the device model, and they canbe embedded into other structures to provide the functionality of the device model. kobject represents an object in the device model, while kset is a collection of related kobjects.
- Attribute filesplay an important role in the device model, and theyare used for data exchange between kernel space and user space. Attribute files can be represented as files in user space through the sysfs virtual file system. Users can read or write these files to interact with the device model. Attribute files allow users to access device status, configuration, and control information, thereby enabling management and configuration of the device model.
- The sysfs virtual file system plays a key role in the device model, as it can present the organizational hierarchy of the device model. Through sysfs, objects, attributes, and relationships in the device model can be represented in user space as directories and files. This organizational form allows users to browse and manage the device model in a hierarchical manner, making it convenient to obtain device information, configuration, and status. sysfs provides a unified interface that enables users to interact with the device model through file system operations, offering visualization and operability of the device model.
Analysis of the platform bus registration process
During the initialization process, the kernel callsplatform_bus_init()a function to initialize the platform bus. The call flow is as follows:
1234 | kernel_init_freeable() ->do_basic_setup() ->driver_init() ->platform_bus_init() |
platform_bus_initThe function is as follows
123456789101112131415161718 | //drivers/base/platform.cint __init platform_bus_init(void){ int error; early_platform_cleanup();// Clean up platform bus related resources in advance error = device_register(&platform_bus);// Register the platform bus device if (error) { put_device(&platform_bus); // If registration fails, release the platform bus device return error; } error = bus_register(&platform_bus_type);// Register the platform bus type if (error) device_unregister(&platform_bus);// If registration fails, unregister the platform bus device of_platform_register_reconfig_notifier();// Register the platform reconfiguration notifier return error;} |
struct bus_type platform_bus_type
platform_bus_typedefined as follows
12345678910111213141516171819202122 | // drivers/base/platform.cstatic const struct dev_pm_ops platform_dev_pm_ops = { .runtime_suspend = pm_generic_runtime_suspend, .runtime_resume = pm_generic_runtime_resume, USE_PLATFORM_PM_SLEEP_OPS};struct bus_type platform_bus_type = { // Specify the name of the platform bus type as "platform" .name = "platform", // Specify the device group pointer, used to define device attribute groups related to the platform bus .dev_groups = platform_dev_groups, // Specify the pointer to the match function, used to determine whether a device is compatible with the platform bus .match = platform_match, // Specify the pointer to the event handler function, used to handle events related to the platform bus .uevent = platform_uevent, // Specify the pointer to the DMA configuration function, used to configure DMA on the platform bus .dma_configure = platform_dma_configure, // Specify the pointer to power management related operation functions, used to manage device power on the platform bus .pm = &platform_dev_pm_ops,};EXPORT_SYMBOL_GPL(platform_bus_type); |
platform_match()
platform_matchIt is a function used to determine whether a device and a driver match. It accepts two parameters:
dev represents the device object pointer
drv represents the driver object pointer
123456789101112131415161718192021222324 | static int platform_match(struct device *dev, struct device_driver *drv){ struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* When driver_override is set, only bind to the matching driver */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0);} |
- First, convert dev and drv respectively to
struct platform_deviceandstruct platform_driverpointers of the type, for subsequent use. - Check
pdev->driver_overridewhether it is set. If it is set, it means that as long as the specified driver name matches, the device and driver can be considered matched. The function comparespdev->driver_overrideanddrv->namewhether the strings are equal. If they are equal, it returns a match (non-zero). - if
pdev->driver_overrideIf not set, first attempt OF-style matching (Open Firmware). Callof_driver_match_device(dev, drv)function, which checks whether the device matches the driver. If the match succeeds, it returns a match (non-zero). - If OF-style matching fails, next try ACPI-style matching (Advanced Configuration and Power Interface). Call
acpi_driver_match_device(dev, drv)function, which checks whether the device matches the driver. If the match succeeds, it returns a match (non-zero). - If ACPI-style matching also fails, finally try to match based on the driver’s ID table. Check whether pdrv->id_table exists. If it exists, call
platform_match_id(pdrv->id_table, pdev)function to check whether the device matches any entry in the ID table. If the match succeeds, it returns a match (non-zero). - If all the above matching attempts fail, finally useDriver nameanddevice namefor comparison. Compare
pdev->nameanddrv->namewhether the strings are equal. If they are equal, it returns a match (non-zero).
From the above analysis, we can see why, in the platform bus matching priority, the matching priority is:of_match_table > id_table > name。
Can be used
MODULE_DEVICE_TABLE(type, name);Device ID Tableplatform_driver.driver.of_match_tableexported to the module’s ELF section, allowing userspace (udev/modprobe) to automatically load the driver. For example:
12345 MODULE_DEVICE_TABLE(of, my_of_match);MODULE_DEVICE_TABLE(i2c, my_i2c_id);MODULE_DEVICE_TABLE(spi, my_spi_id);MODULE_DEVICE_TABLE(usb, my_usb_id);MODULE_DEVICE_TABLE(pci, my_pci_id);
Registering a device under the bus
First, add under the custom bus module
1 | EXPORT_SYMBOL_GPL(mybus);// Export bus symbols |
Add device:
123456789101112131415161718192021222324252627282930313233343536 | extern struct bus_type mybus;void mydev_release(struct device *dev){ pr_info("%s\n", __func__);}struct device mydevice = { .init_name = "mydevice", .bus = &mybus, .release = mydev_release, .devt = ((255 << 20) | 0),};static int __init device_test_init(void){ int ret; ret = device_register(&mydevice); return ret;}static void __exit device_test_exit(void){ device_unregister(&mydevice);}module_init(device_test_init);module_exit(device_test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a test sample for my own bus"); |
Because
mybusis also a module driver, so you need to setKBUILD_EXTRA_SYMBOLSpointing to the compiledmybusgenerated by the moduleModule.symvers, otherwise an error will be reported:ERROR: modpost: "mybus" undefined!, for exampleKBUILD_EXTRA_SYMBOLS := $(PWD)/../55_create_attr_under_my_own_bus/Module.symvers
Test:
1234567891011121314151617181920212223242526272829303132 | $ insmod create_attr_under_my_own_bus.ko[ 21.174906] create_attr_under_my_own_bus: loading out-of-tree module taints kernel.$ insmod register_device_under_my_own_bus.ko$ ls /sys/busamba gpio mmc_rpmb serialclockevents hid mybus socclocksource i2c nvmem spicontainer iscsi_flashnode pci usbcpu mdio_bus platform workqueueevent_source mipi-dsi scsigenpd mmc sdio$ ls /sys/bus/mybusdevices drivers_autoprobe ueventdrivers drivers_probe value$ ls /sys/bus/mybus/devices/mydevice$ ls /sys/bus/mybus/devices/mydevice/dev power subsystem uevent$ lsmodregister_device_under_my_own_bus 16384 0 - Live 0xffffffc008b35000 (O)create_attr_under_my_own_bus 16384 1 register_device_under_my_own_bus, Live 0xffffffc008b30000 (O)$ rmmod register_device_under_my_own_bus.ko[ 127.412626] mydev_release$ lsmodcreate_attr_under_my_own_bus 16384 0 - Live 0xffffffc008b30000 (O)$ ls /sys/bus/mybus/devices/$ ls /sys/bus/mybus/devices drivers_autoprobe ueventdrivers drivers_probe value$ rmmod create_attr_under_my_own_bus.ko$ ls /sys/bus/mybus/ls: /sys/bus/mybus/: No such file or directory |
Device registration process analysis
device_register()
1234567 | // drivers/base/core.cint device_register(struct device *dev){ device_initialize(dev); return device_add(dev);}EXPORT_SYMBOL_GPL(device_register); |
device_initialize()
12345678910111213141516171819202122232425262728293031323334 | // drivers/base/core.cvoid device_initialize(struct device *dev){ // The code sets the kobj.kset member of the device object to devices_kset, indicating that the kset to which the device object belongs is devices_kset, that is, the device object belongs to the devices subsystem dev->kobj.kset = devices_kset; // Call kobject_init function to initialize the kobj member of the device object, using device_ktype as the ktype. Through this function call, the kobject of the device object is correctly initialized and set. kobject_init(&dev->kobj, &device_ktype); // Use INIT_LIST_HEAD macro initializes the device object's DMA_pools、msi_list、consumers、suppliers、needs_suppliers and defer_hook and other list heads to ensure they are empty linked lists INIT_LIST_HEAD(&dev->dma_pools); // Call the mutex_init function to initialize the mutex of the device object, used for mutually exclusive operations on the device. mutex_init(&dev->mutex); mutex_init(&dev->lockdep_mutex); // via lockdep_set_The novalidate_class function sets the validation class of dev->mutex to invalid, so as to avoid the deadlock analyzer validating the mutex. lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); // Initialize power management related information of the device object device_pm_init(dev); // Indicates that no device node is specified set_dev_node(dev, -1); raw_spin_lock_init(&dev->msi_lock); INIT_LIST_HEAD(&dev->msi_list); INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.needs_suppliers); INIT_LIST_HEAD(&dev->links.defer_hook); // The code sets the status member of the device object to DL_DEV_NO_DRIVER, indicating that the device currently has no driver dev->links.status = DL_DEV_NO_DRIVER;}EXPORT_SYMBOL_GPL(device_initialize); |
device_add()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 | // drivers/base/core.cint device_add(struct device *dev){ struct device *parent; struct kobject *kobj; struct class_interface *class_intf; int error = -EINVAL; struct kobject *glue_dir = NULL; // Get a reference to the device dev = get_device(dev); if (!dev) goto done; if (!dev->p) { // If the device's private data is not initialized, initialize it error = device_private_init(dev); if (error) goto done; } /* * for statically allocated devices, which should all be converted * some day, we need to initialize the name. We prevent reading back * the name, and force the use of dev_name() */ if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name);// Initialize the device's name dev->init_name = NULL; } /* subsystems can specify simple device enumeration */ if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);// If the device name is empty and the name of the bus to which the device belongs is not empty, set the device name if (!dev_name(dev)) { error = -EINVAL; goto name_error; } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); parent = get_device(dev->parent);// Get a reference to the device's parent device kobj = get_device_parent(dev, parent);// Get the device's parent kobject if (IS_ERR(kobj)) { error = PTR_ERR(kobj); goto parent_error; } if (kobj) dev->kobj.parent = kobj; /* use parent numa_node */ if (parent && (dev_to_node(dev) == NUMA_NO_NODE)) set_dev_node(dev, dev_to_node(parent));// Use the parent device's NUMA node /* first, register with generic layer. */ /* we require the name to be set before, and pass NULL */ // First, register the device with the generic layer // Need to set the device name before this, and set parent to NULL error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); if (error) { glue_dir = get_glue_dir(dev); goto Error; } /* notify platform of device entry */ // Notify the addition of the platform device error = device_platform_notify(dev, KOBJ_ADD); if (error) goto platform_error; // Create the device's uevent attribute file error = device_create_file(dev, &dev_attr_uevent); if (error) goto attrError; // Add a symbolic link for the device class error = device_add_class_symlinks(dev); if (error) goto SymlinkError; // Add device attributes error = device_add_attrs(dev); if (error) goto AttrsError; // Add the device to the bus error = bus_add_device(dev); if (error) goto BusError; // Add the device in the device power management directory error = dpm_sysfs_add(dev); if (error) goto DPMError; // Add device to power management device_pm_add(dev); // If the device's devt has a major device number if (MAJOR(dev->devt)) { // Create the device's dev attribute file error = device_create_file(dev, &dev_attr_dev); if (error) goto DevAttrError; // Create the device's sys device node error = device_create_sys_dev_entry(dev); if (error) goto SysEntryError; // Create device node on devtmpfs devtmpfs_create_node(dev); } /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ if (dev->bus)// Notify the device addition event chain blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); /* * Check if any of the other devices (consumers) have been waiting for * this device (supplier) to be added so that they can create a device * link to it. * * This needs to happen after device_pm_add() because device_link_add() * requires the supplier be registered before it's called. * * But this also needs to happen before bus_probe_device() to make sure * waiting consumers can link to it before the driver is bound to the * device and the driver sync_state callback is called for this device. */ // Check whether other devices (consumers) have been waiting for the addition of this device (supplier) so that device links can be created. if (dev->fwnode && !dev->fwnode->dev) { dev->fwnode->dev = dev; fw_devlink_link_device(dev); } // Probe the devices on the bus bus_probe_device(dev); if (parent)// If a parent device exists, add the current device to the parent device's child device list klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); // If the device has a class if (dev->class) { mutex_lock(&dev->class->p->mutex); /* tie the class to the device */ klist_add_tail(&dev->p->knode_class, &dev->class->p->klist_devices);// Add the device to the class's device list /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->p->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf);// Notify any interface that the device has been added mutex_unlock(&dev->class->p->mutex); }done: put_device(dev);// Release the device reference return error; SysEntryError: if (MAJOR(dev->devt))// If a major device number exists, remove the device's dev attribute file device_remove_file(dev, &dev_attr_dev); DevAttrError: device_pm_remove(dev);// Remove the device's power management dpm_sysfs_remove(dev);// Remove the device from the device power management directory DPMError: bus_remove_device(dev);// Remove the device from the bus BusError:// Remove the device's attributes device_remove_attrs(dev); AttrsError:// Remove the device class symlink device_remove_class_symlinks(dev); SymlinkError:// Remove the device's uevent attribute file device_remove_file(dev, &dev_attr_uevent); attrError: device_platform_notify(dev, KOBJ_REMOVE);platform_error: kobject_uevent(&dev->kobj, KOBJ_REMOVE);// Send KOBJ_REMOVE event for the device's kobject glue_dir = get_glue_dir(dev);// Get the device's glue directory kobject_del(&dev->kobj);// Delete the device's kobject Error: cleanup_glue_dir(dev, glue_dir);// Clean up the device's glue directoryparent_error: put_device(parent);// Release the parent device referencename_error: kfree(dev->p);// Release the device's private data dev->p = NULL; goto done;}EXPORT_SYMBOL_GPL(device_add); |
The above code usesbus_add_devicefunction to add the device to the bus
bus_add_device()
123456789101112131415161718192021222324252627282930313233343536373839 | // drivers/base/bus.c/** * bus_add_device - add device to bus * @dev: device being added * * - Add device's bus attributes. * - Create links to device's bus. * - Add the device to its bus's list of devices. */int bus_add_device(struct device *dev){ struct bus_type *bus = bus_get(dev->bus);// Get the pointer to the bus type (bus_type) to which the device belongs int error = 0; if (bus) {// If the bus type pointer is successfully obtained pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev)); error = device_add_groups(dev, bus->dev_groups);// Add the device to the device groups (dev_groups) of the bus type if (error) goto out_put; error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev));// Create a symbolic link for the device under the kernel object (kobj) of the bus type's device set (kset) if (error) goto out_groups; error = sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem");// Create a symbolic link pointing to the bus type subsystem (subsystem) under the device's kernel object (kobj) if (error) goto out_subsys; klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);// Add the device's node to the device list of the bus type } return 0;out_subsys: sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));out_groups: device_remove_groups(dev, bus->dev_groups);out_put: bus_put(dev->bus); return error;} |
sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev))
In the device set of the bus type (devices_kset) under the kernel object (kobj), create a symbolic link for the device. This symbolic link links the device’s sysfs directory to the device set directory of the bus type.sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem")
Create a symbolic link pointing to the bus type subsystem (subsystem) under the device’s kernel object (kobj). This symbolic link links the device’s sysfs directory to the directory of the bus type subsystem.
Analysis of the platform bus device registration process
In the platform device driver, we useplatform_device_registerRegister the device
platform_device_register()
12345678910111213 | // drivers/base/platform.c/** * platform_device_register - add a platform-level device * @pdev: platform device we're adding */int platform_device_register(struct platform_device *pdev){ device_initialize(&pdev->dev); setup_pdev_dma_masks(pdev); return platform_device_add(pdev);}EXPORT_SYMBOL_GPL(platform_device_register); |
platform_device_add()
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 | /** * platform_device_add - add a platform device to device hierarchy * @pdev: platform device we're adding * * This is part 2 of platform_device_register(), though may be called * separately _iff_ pdev was allocated by platform_device_alloc(). */int platform_device_add(struct platform_device *pdev){ u32 i; int ret; if (!pdev)// Check whether the input platform device pointer is NULL return -EINVAL; if (!pdev->dev.parent)// If the parent device of the platform device is NULL, set the parent device to platform_bus pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type;// Set the bus of the platform device to platform_bus_type switch (pdev->id) {// Perform different processing according to the id of the platform device default: dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);// Set the device name based on the device name and id break; case PLATFORM_DEVID_NONE: // If the id is PLATFORM_DEVID_NONE, then only use the device name as the name of the device dev_set_name(&pdev->dev, "%s", pdev->name); break; case PLATFORM_DEVID_AUTO: /* * Automatically allocated device ID. We mark it as such so * that we remember it must be freed, and we append a suffix * to avoid namespace collision with explicit IDs. */ /* * Automatically allocated device ID。Mark it as automatically allocated,so that we remember it needs to be released, * and to avoid conflict with explicit ID namespace conflict,We append a suffix。 */ ret = ida_alloc(&platform_devid_ida, GFP_KERNEL); if (ret < 0) goto err_out; pdev->id = ret; pdev->id_auto = true; dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); break; } // Iterate over the platform device's resource list and process each resource for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; // If the resource name is empty, set the resource name to the device name if (r->name == NULL) r->name = dev_name(&pdev->dev); p = r->parent; if (!p) { // If the resource does not specify a parent resource, set a default parent resource based on the resource type if (resource_type(r) == IORESOURCE_MEM) p = &iomem_resource; else if (resource_type(r) == IORESOURCE_IO) p = &ioport_resource; } if (p) { // If the parent resource exists and inserting the resource into the parent resource fails, return an error ret = insert_resource(p, r); if (ret) { dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r); goto failed; } } } pr_debug("Registering platform device '%s'. Parent at %s\n", dev_name(&pdev->dev), dev_name(pdev->dev.parent)); // Add the device to the device hierarchy and register the device ret = device_add(&pdev->dev); if (ret == 0) return ret; failed: if (pdev->id_auto) {// If the device ID is automatically allocated, the allocated ID needs to be removed ida_free(&platform_devid_ida, pdev->id); pdev->id = PLATFORM_DEVID_AUTO; } while (i--) {// In case of failure, release the inserted resources struct resource *r = &pdev->resource[i]; if (r->parent) release_resource(r); } err_out: return ret;}EXPORT_SYMBOL_GPL(platform_device_add); |
Perform different processing based on the platform device ID:
- By default, set the device name based on the device name and ID.
- If the ID is PLATFORM_DEVID_NONE, then only use the device name as the device’s name.
- If the ID is PLATFORM_DEVID_AUTO, then automatically allocate a device ID. Use
ida_allocfunction to obtain an available ID, and mark the device ID as automatically allocated. A suffix will be appended to the device name to avoid namespace conflicts with explicit IDs.
Set the device name. The name has three formats, as shown in the figure below.


Why register the device before registering the bus?
Before registering the platform device, it will first calldevice_register()function to register a deviceplatform_bus。
12345678910111213141516171819 | //drivers/base/platform.cint __init platform_bus_init(void){ int error; early_platform_cleanup(); error = device_register(&platform_bus); if (error) { put_device(&platform_bus); return error; } error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); of_platform_register_reconfig_notifier(); return error;} |
It can be seen that it first callsdevice_register(&platform_bus)then calls bus_register(&platform_bus_type)
first callsdevice_registerfunction registrationplatform_busThis device will, in/sys/devicescreate a directory under the directory/sys/devices/platform, the created /sys/devices/platformdirectory is the parent directory of all platform devices.
That is, allplatform_deviceall devices will/sys/devices/platformcreate subdirectories under it, as shown in the following figure:

Registering a driver under the bus
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 | extern struct bus_type mybus;int mydriver_remove(struct device *dev){ printk("This is mydriver_remove\n"); return 0;};int mydriver_probe(struct device *dev){ printk("This is mydriver_probe\n"); return 0;};struct device_driver mydriver = { .name = "mydevice", .bus = &mybus, .probe = mydriver_probe, .remove = mydriver_remove,};// Module initialization functionstatic int mydriver_init(void){ int ret; ret = driver_register(&mydriver); return ret;}// Module exit functionstatic void mydriver_exit(void){ driver_unregister(&mydriver);}module_init(mydriver_init); // Specify the module's initialization functionmodule_exit(mydriver_exit); // Specify the module's exit functionMODULE_LICENSE("GPL"); // The license used by the moduleMODULE_AUTHOR("topeet"); // The module's author |
Test:
12345678910111213 | $ insmod create_attr_under_my_own_bus.ko[ 13.087171] create_attr_under_my_own_bus: loading out-of-tree module taints kernel.$ insmod register_device_under_my_own_bus.ko$ insmod register_driver_under_my_own_bus.ko[ 35.109914] Driver 'mydevice' needs updating - please use bus_type methods[ 35.110974] mydriver_probe$ ls /sys/bus/mybus/devices drivers_autoprobe ueventdrivers drivers_probe value$ ls /sys/bus/mybus/driversmydevice$ ls /sys/bus/mybus/devices/mydevice |
Printed when loading the driver in the above testDriver 'mydevice' needs updating - please use bus_type methods, this is because in mybus’sprobefunction calleddrv->probe(dev), this is because indriver_register()In the function:
12345 | if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) pr_warn("Driver '%s' needs updating - please use " "bus_type methods\n", drv->name); |
That is, ifstruct bus_typea probe function is defined in,struct device_driverthis function is also defined and will also
Comment outmybusthe probe function and test again:
12345 | ~ # insmod create_attr_under_my_own_bus.ko[ 28.767545] create_attr_under_my_own_bus: loading out-of-tree module taints kernel.~ # insmod register_device_under_my_own_bus.ko~ # insmod register_driver_under_my_own_bus.ko[ 39.782320] mydriver_probe |
Actually
bus_type.probeHistorically used for certain special buses (such as early versions of the platform bus), but modern kernels have treated it as a legacy interface.
such asdrivers/base/platform. cdefined as follows:
123456789 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,};EXPORT_SYMBOL_GPL(platform_bus_type);And in the
drivers/base/dd.cinreally_probe()The function will calldrv->probefunction:
123456789 if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed;} else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed;}
Driver registration process analysis
driver_register()
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 | // drivers/base/driver.c/** * driver_register - register driver with bus * @drv: driver to register * * We pass off most of the work to the bus_add_driver() call, * since most of the things we have to do deal with the bus * structures. */int driver_register(struct device_driver *drv){ int ret; struct device_driver *other; // Check whether the bus has been initialized. if (!drv->bus->p) { pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n", drv->name, drv->bus->name); return -EINVAL; } // Check whether the driver's methods need to be updated. if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) pr_warn("Driver '%s' needs updating - please use " "bus_type methods\n", drv->name); // Check whether the driver has already been registered. other = driver_find(drv->name, drv->bus); if (other) { pr_err("Error: Driver '%s' is already registered, " "aborting...\n", drv->name); return -EBUSY; } ret = bus_add_driver(drv);// Add the driver to the bus. if (ret) return ret; ret = driver_add_groups(drv, drv->groups);// Add the driver's group attributes. if (ret) { bus_remove_driver(drv);// Remove the added driver. return ret; } kobject_uevent(&drv->p->kobj, KOBJ_ADD);// Send a kernel object event to notify that the driver was added successfully. return ret;}EXPORT_SYMBOL_GPL(driver_register); |
driver_registerThe function is used to register a device driver and add it to the bus. The following is an explanation of the function’s functionality:
- Lines 16 to 20: Check whether the bus has been initialized: first, by
drv->busaccessing the bus information in the device driver structure. If the bus’s p member is NULL, it indicates that the bus is not initialized. If the bus is not initialized, print an error message and return-EINVALan error code indicating an invalid parameter. - Lines 22 to 26: Check whether the driver’s methods need to be updated: by checking the
bus->probeanddrv->probe、bus->removeanddrv->remove、bus->shutdownanddrv->shutdownmembers in the driver structure to determine whether they exist simultaneously. If there is a method combination that needs updating, it indicates that the driver needs to be updated. In this case, print a warning message and recommend usingbus_typethe defined method to perform the update. - Lines 28 to 33: Check whether the driver has already been registered: call
driver_findthe function to find whether a driver with the same name has already been registered. If a driver with the same name is found, it indicates that the driver has already been registered. In this case, print an error message and return-EBUSYan error code indicating that the device is busy. - Line 35: Add the driver to the bus: call
bus_add_driverthe function to add the driver to the bus. If the addition fails, return the corresponding error code. - Line 38: Add the driver’s group attributes: call
driver_add_groupsthe function to add the driver’s group attributes to the driver. If the addition fails, then
Callbus_remove_driverthe function removes the added driver and returns the corresponding error code. - Line 43: Send a kernel object event: call
kobject_ueventthe function sends an event to the driver’s kernel object to notify that the driver has been successfully added to the system.
In summary,driver_registerThis function registers the device driver and adds it to the bus, while performing various checks and error handling operations.
bus_add_driver()
In the above code, callingbus_add_driverfunction adds the driver to the bus. Let’s analyze it in detail.bus_add_driverfunction
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 | // drivers/base/bus.c/** * bus_add_driver - Add a driver to the bus. * @drv: driver. */int bus_add_driver(struct device_driver *drv){ struct bus_type *bus; struct driver_private *priv; int error = 0; // Get the bus object bus = bus_get(drv->bus); if (!bus) return -EINVAL; pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name); // Allocate and initialize driver private data priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices, NULL, NULL); priv->driver = drv; drv->p = priv; priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);// Initialize and add the driver's kernel object if (error) goto out_unregister; // Add the driver to the bus's driver list klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); if (drv->bus->p->drivers_autoprobe) {// If the bus has auto-probing enabled, attempt to auto-probe devices error = driver_attach(drv); if (error) goto out_del_list; } module_add_driver(drv->owner, drv);// Add the driver to the module // Create the driver's uevent attribute file error = driver_create_file(drv, &driver_attr_uevent); if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed\n", __func__, drv->name); } error = driver_add_groups(drv, bus->drv_groups);// Add the driver's group attributes. if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_create_groups(%s) failed\n", __func__, drv->name); } if (!drv->suppress_bind_attrs) {// If the driver does not disable the bind attribute file, add the bind attribute file error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed\n", __func__, drv->name); } } return 0;out_del_list: klist_del(&priv->knode_bus);out_unregister: kobject_put(&priv->kobj); /* drv->p is freed in driver_release() */ drv->p = NULL;out_put_bus: bus_put(bus); return error;} |
bus_add_driverThis function is used to add a device driver to the bus. The following is a detailed explanation of its functionality:
- Line 13 gets the bus object: by
drv->busaccessing the bus information in the device driver structure. By callingbus_getfunction gets the bus object. If the bus object does not exist, it returns-EINVALan error code indicating an invalid parameter. - Lines 20 to 24 allocate and initialize driver private data: calling
kzallocfunction allocates memory for the driver’s private data structure priv, and usesGFP_KERNELflag for memory allocation. If memory allocation fails, it returns the -ENOMEM error code indicating insufficient memory. - Lines 25 to 27 use
klist_initfunction initializes the device list in the priv structure. Set the driver pointer in the priv structure and assign it to the current driver. Pointdrv->pto the priv structure for subsequent release operations. - Lines 29 to 32 initialize and add the driver’s kernel object: set
priv->kobj.ksetmember to the bus object’sdrivers_kset. When callingkobject_init_and_addThe function initializes and adds the driver’s kernel object. If initialization or addition fails, jump toout_unregisterperform error handling. - Line 35 adds the driver to the bus’s driver list: using
klist_add_tailthe function adds the driver’s node to the bus’s driver list
Analysis of the probe function execution flow
Above, inbus_add_driverthe function, if the bus has auto-probing enabled (drivers_autoprobeflag), then calldriver_attachthe function attempts to auto-probe the device.
If auto-probing fails, jump to out_unregister for error handling.
variabledrivers_autoprobeIt can also be, in user space, through the attribute filedrivers_autoprobeto control, once again reflecting the role of the attribute file

The entire process is shown in the following figure:

driver_attach()
1234567891011121314151617 | //drivers/base/dd.c/** * driver_attach - try to bind driver to devices. * @drv: driver. * * Walk the list of devices that the bus has on it and try to * match the driver with each one. If driver_probe_device() * returns 0 and the @dev->driver is set, we've found a * compatible pair. */int driver_attach(struct device_driver *drv){ //bus_for_The each_dev() function mainly provides a shortcut for traversing the list of device objects on a specified bus, // and performing specific operations on each device object. It can be used in scenarios where the driver needs to manage and operate a large number of device instances. return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);}EXPORT_SYMBOL_GPL(driver_attach); |
__driver_attach()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 | static int __driver_attach(struct device *dev, void *data){ struct device_driver *drv = data;// The passed data parameter data serves as the device driver object bool async = false; int ret; /* * Lock device and try to bind to it. We drop the error * here and always return 0, because we need to keep trying * to bind to devices and some drivers will return an error * simply if it didn't support the device. * * driver_probe_device() will spit a warning if there * is an error. */ ret = driver_match_device(drv, dev);// Attempt to bind the driver to the device if (ret == 0) { /* no match */ return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); driver_deferred_probe_add(dev);// Request to defer probing the device /* * Driver could not match with device, but may match with * another device on the bus. */ return 0; } else if (ret < 0) {// The bus cannot match the device, return an error code dev_dbg(dev, "Bus failed to match device: %d\n", ret); /* * Driver could not match with device, but may match with * another device on the bus. */ return 0; } /* ret > 0 means positive match */ if (driver_allows_async_probing(drv)) { /* * Instead of probing the device synchronously we will * probe it asynchronously to allow for more parallelism. * * We only take the device lock here in order to guarantee * that the dev->driver and async_driver fields are protected */ dev_dbg(dev, "probing driver %s asynchronously\n", drv->name); device_lock(dev);// Lock the device to protect the dev->driver and async_driver fields if (!dev->driver) { get_device(dev); dev->p->async_driver = drv;// Set the device's asynchronous driver async = true; } device_unlock(dev); if (async) async_schedule_dev(__driver_attach_async_helper, dev);// Asynchronously schedule the driver's additional processing function return 0; } device_driver_attach(drv, dev);// Synchronously probe the device and bind the driver return 0;} |
driver_match_device()
123456 | // drivers/base/base.hstatic inline int driver_match_device(struct device_driver *drv, struct device *dev){ return drv->bus->match ? drv->bus->match(dev, drv) : 1;} |
If the device and driver match, it will continue to executedevice_driver_attachfunction
device_driver_attach()
1234567891011121314151617181920212223242526 | // drivers/base/dd.c/** * device_driver_attach - attach a specific driver to a specific device * @drv: Driver to attach * @dev: Device to attach it to * * Manually attach driver to a device. Will acquire both @dev lock and * @dev->parent lock if needed. */int device_driver_attach(struct device_driver *drv, struct device *dev){ int ret = 0; __device_driver_lock(dev, dev->parent); /* * If device has been removed or someone has already successfully * bound a driver before us just skip the driver probe call. */ if (!dev->p->dead && !dev->driver) ret = driver_probe_device(drv, dev); __device_driver_unlock(dev, dev->parent); return ret;} |
driver_probe_device()
1234567891011121314151617181920212223242526272829303132333435363738394041 | /** * driver_probe_device - attempt to bind device & driver together * @drv: driver to bind a device to * @dev: device to try to bind to the driver * * This function returns -ENODEV if the device is not registered, * 1 if the device is bound successfully and 0 otherwise. * * This function must be called with @dev lock held. When called for a * USB interface, @dev->parent lock must be held as well. * * If the device has a parent, runtime-resume the parent before driver probing. */int driver_probe_device(struct device_driver *drv, struct device *dev){ int ret = 0; if (!device_is_registered(dev))// Check whether the device is registered; if not, return error code -ENODEV return -ENODEV; // Print debug information indicating that the device matches the driver pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); // Get the runtime reference count of the device supplier pm_runtime_get_suppliers(dev); if (dev->parent)// If the device has a parent device, get the parent device's synchronous runtime reference count pm_runtime_get_sync(dev->parent); pm_runtime_barrier(dev);// Wait for the device's runtime state to become stable if (initcall_debug)// Select and call the real probe function based on the initialization debug flag ret = really_probe_debug(dev, drv); else ret = really_probe(dev, drv); pm_request_idle(dev);// Request the device to enter idle state (power-saving mode) if (dev->parent)// If the device has a parent device, release the parent device's runtime reference count pm_runtime_put(dev->parent); pm_runtime_put_suppliers(dev);// Release the runtime reference count of the device supplier return ret;} |
really_probe()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 | // drivers/base/dd.cstatic int really_probe(struct device *dev, struct device_driver *drv){ int ret = -EPROBE_DEFER;// Initialize the return value to deferred probe int local_trigger_count = atomic_read(&deferred_trigger_count);// Get the current deferred probe count bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) && !drv->suppress_bind_attrs; if (defer_all_probes) { /* * Value of defer_all_probes can be set only by * device_block_probing() which, in turn, will call * wait_for_device_probe() right after that to avoid any races. */ dev_dbg(dev, "Driver %s force probe deferral\n", drv->name); driver_deferred_probe_add(dev); return ret; } ret = device_links_check_suppliers(dev);// Check the device's supplier link if (ret == -EPROBE_DEFER) driver_deferred_probe_add_trigger(dev, local_trigger_count);// Add the device to the deferred probe trigger list if (ret) return ret; atomic_inc(&probe_count);// Increment the probe count pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); if (!list_empty(&dev->devres_head)) { dev_crit(dev, "Resources present before probing\n"); ret = -EBUSY; goto done; }re_probe: dev->driver = drv; /* If using pinctrl, bind pins now before probing */ ret = pinctrl_bind_pins(dev);/* If pinctrl is used, bind the pins */ if (ret) goto pinctrl_bind_failed; if (dev->bus->dma_configure) {// Configure DMA ret = dev->bus->dma_configure(dev); if (ret) goto probe_failed; } ret = driver_sysfs_add(dev);// Add the driver's sysfs if (ret) { pr_err("%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); goto probe_failed; } if (dev->pm_domain && dev->pm_domain->activate) {// If the device has a power management domain and an activation function exists, activate the power management domain ret = dev->pm_domain->activate(dev); if (ret) goto probe_failed; } if (dev->bus->probe) {// If the bus has a probe function, call the bus's probe function ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) {// Otherwise, call the driver's probe function ret = drv->probe(dev); if (ret) goto probe_failed; } ret = device_add_groups(dev, drv->dev_groups); if (ret) { dev_err(dev, "device_add_groups() failed\n"); goto dev_groups_failed; } if (dev_has_sync_state(dev)) { ret = device_create_file(dev, &dev_attr_state_synced); if (ret) { dev_err(dev, "state_synced sysfs add failed\n"); goto dev_sysfs_state_synced_failed; } } if (test_remove) {// If driver removal test is enabled test_remove = false; device_remove_file(dev, &dev_attr_state_synced); device_remove_groups(dev, drv->dev_groups); if (dev->bus->remove)// If the bus has a remove function, call the bus's remove function dev->bus->remove(dev); else if (drv->remove)// Otherwise, call the driver's remove function drv->remove(dev); devres_release_all(dev);// Release the device's resources arch_teardown_dma_ops(dev);// Remove the driver's sysfs kfree(dev->dma_range_map); dev->dma_range_map = NULL; driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); if (dev->pm_domain && dev->pm_domain->dismiss)// If the device has a power management domain and a detach function exists, detach the power management domain dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev);// Reinitialize power management runtime goto re_probe;// Re-probe } pinctrl_init_done(dev);// Complete the initialization of pinctrl if (dev->pm_domain && dev->pm_domain->sync)// If the device has a power management domain and a sync function exists, synchronize the power management domain dev->pm_domain->sync(dev); driver_bound(dev);// Driver binding succeeded ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); goto done;dev_sysfs_state_synced_failed: device_remove_groups(dev, drv->dev_groups);dev_groups_failed: if (dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) drv->remove(dev);probe_failed: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev);pinctrl_bind_failed: device_links_no_driver(dev);// Unbind the device from the driver devres_release_all(dev);// Release the device's resources arch_teardown_dma_ops(dev);// Cancel DMA configuration kfree(dev->dma_range_map); dev->dma_range_map = NULL; driver_sysfs_remove(dev);// Remove the driver's sysfs dev->driver = NULL; dev_set_drvdata(dev, NULL); if (dev->pm_domain && dev->pm_domain->dismiss)// If the device has a power management domain and a detach function exists, detach the power management domain dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev);// Reinitialize power management runtime dev_pm_set_driver_flags(dev, 0);// Set the device's driver flag to 0 switch (ret) { case -EPROBE_DEFER:/* The driver requests deferred probing */ /* Driver requested deferred probing */ dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name); driver_deferred_probe_add_trigger(dev, local_trigger_count);// Add the device to the deferred probe trigger list break; case -ENODEV: case -ENXIO: pr_debug("%s: probe of %s rejects match %d\n", drv->name, dev_name(dev), ret); break; default:/* The driver matched but probe failed */ /* driver matched but the probe failed */ pr_warn("%s: probe of %s failed with error %d\n", drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0;done: atomic_dec(&probe_count);// Decrement the probe count wake_up_all(&probe_waitqueue);// Wake up processes waiting for probe return ret;} |
Analysis of the order of loading driver and device
Based on the previous analysis, regardless of whether we load firstdevice.koordriver.koBoth the driver and the device can match successfully. So we can guess that whether it is device-driven or driver-driven, there will be a matching operation.
device_add()
Beforedrivers/base/core.cin the filedevice_addThe function callsbus_probe_devicefunction
1234567891011121314151617 | // drivers/base/core.cint device_add(struct device *dev){ ... // Probe the devices on the bus bus_probe_device(dev); if (parent)// If a parent device exists, add the current device to the parent device's child device list klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); // If the device has a class if (dev->class) { mutex_lock(&dev->class->p->mutex); ...}EXPORT_SYMBOL_GPL(device_add); |
bus_probe_device()
bus_probe_deviceThe most important thing in a function isdevice_initial_probefunction
12345678910111213141516171819202122232425 | // drivers/base/bus.c/** * bus_probe_device - probe drivers for a new device * @dev: device to probe * * - Automatically probe for a driver if the bus allows it. */void bus_probe_device(struct device *dev){ struct bus_type *bus = dev->bus; struct subsys_interface *sif; if (!bus) return; if (bus->p->drivers_autoprobe) device_initial_probe(dev); mutex_lock(&bus->p->mutex); list_for_each_entry(sif, &bus->p->interfaces, node) if (sif->add_dev) sif->add_dev(dev, sif); mutex_unlock(&bus->p->mutex);} |
device_initial_probe()
device_initial_probeCall__device_attach
123456 | // drivers/base/dd.cvoid device_initial_probe(struct device *dev){ __device_attach(dev, true);} |
__device_attach()
__device_attachas follows
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 | // drivers/base/dd.cstatic int __device_attach(struct device *dev, bool allow_async){ int ret = 0; bool async = false; device_lock(dev); if (dev->p->dead) { goto out_unlock; } else if (dev->driver) { if (device_is_bound(dev)) {// If the device is already bound to a driver, return 1 ret = 1; goto out_unlock; } ret = device_bind_driver(dev);// Attempt to bind the device to the driver if (ret == 0) ret = 1; else { dev->driver = NULL;// If binding fails, set the device's driver pointer to NULL ret = 0; } } else { struct device_attach_data data = {// If the device has no driver, it is necessary to traverse the drivers on the bus to find a match .dev = dev, .check_async = allow_async, .want_async = false, }; if (dev->parent)// If the device has a parent device, call pm_runtime_get_sync() increments the reference count of the parent device pm_runtime_get_sync(dev->parent); // Traverse the drivers on the bus and call __device_attach_driver() to match ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); if (!ret && allow_async && data.have_async) { /* * If we could not find appropriate driver * synchronously and we are allowed to do * async probes and there are drivers that * want to probe asynchronously, we'll * try them. */ /* * If a suitable driver cannot be found synchronously,and asynchronous probing is allowed and a driver requests asynchronous probing, * then attempt asynchronous probing。 */ dev_dbg(dev, "scheduling asynchronous probe\n"); get_device(dev);// Increment the device's reference count to ensure the device is not released during asynchronous probing async = true; } else { pm_request_idle(dev);// If asynchronous probing is not possible or no driver requests asynchronous probing, call pm_request_idle() to enter the idle state } if (dev->parent)// If the device has a parent device, call pm_runtime_put() decrements the reference count of the parent device pm_runtime_put(dev->parent); }out_unlock: device_unlock(dev); if (async) async_schedule_dev(__device_attach_async_helper, dev);// Schedule an asynchronous task __device_attach_async_helper() to perform asynchronous probing return ret;} |
device_bind_driver()
Used in the above functiondevice_bind_driverBind device driver
123456789101112131415161718192021222324252627 | // drivers/base/dd.c/** * device_bind_driver - bind a driver to one device. * @dev: device. * * Allow manual attachment of a driver to a device. * Caller must have already set @dev->driver. * * Note that this does not modify the bus reference count. * Please verify that is accounted for before calling this. * (It is ok to call with no other effort from a driver's probe() method.) * * This function must be called with the device lock held. */int device_bind_driver(struct device *dev){ int ret; ret = driver_sysfs_add(dev); if (!ret) driver_bound(dev); else if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); return ret;}EXPORT_SYMBOL_GPL(device_bind_driver); |
driver_bound()
1234567891011121314151617181920212223242526272829303132 | static void driver_bound(struct device *dev){ if (device_is_bound(dev)) {// If the device is already bound to a driver, print a warning message and return pr_warn("%s: device %s already bound\n", __func__, kobject_name(&dev->kobj)); return; } pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name, __func__, dev_name(dev)); // Add the device to the driver's device list klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); device_links_driver_bound(dev);// Update the device's driver link status device_pm_check_callbacks(dev);// Check the device's power management callback function /* * Make sure the device is no longer in one of the deferred lists and * kick off retrying all pending devices */ /* * Ensure the device is no longer in the deferred probe list,and start retrying all pending devices */ driver_deferred_probe_del(dev); driver_deferred_probe_trigger(); // If the device has a bus, call the bus notifier chain to notify. if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); // Send kernel object event notification. kobject_uevent(&dev->kobj, KOBJ_BIND);} |
The purpose of the above code is to bind the driver and the device. First, by callingdevice_is_bound(dev)Check whether the device is already bound to a driver.
- If the device is already bound to a driver, output a warning message and return.
- If the device is not bound to a driver, output binding information, including the driver’s name, function name, and device name. Next, by calling
klist_add_tail()Add the device to the driver’s device linked list. In this way,the driver can access all bound devices by traversing the linked list.。
Then, calldevice_links_driver_bound()Update the device’s driver link status. This function ensures that the link relationship between the device and the driver is correct.
Example analysis of the platform bus driver registration process.
platform_driver_register()
12 |
__platform_driver_register()
12345678910111213141516171819 | // drivers/base/platform.c/** * __platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure * @owner: owning module/driver */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);}EXPORT_SYMBOL_GPL(__platform_driver_register); |
driver_registerThe function has been analyzed before, so next we focus on how the probe function of the platform bus is executed:
platform_drv_probe()
123456789101112131415161718192021222324252627282930313233 | static int platform_drv_probe(struct device *_dev){ // Convert the device pointer passed to the driver to a platform_driver structure pointer. struct platform_driver *drv = to_platform_driver(_dev->driver); // Convert the device pointer passed to the driver to a platform_device structure pointer. struct platform_device *dev = to_platform_device(_dev); int ret; // Set the default clock properties of the device node. ret = of_clk_set_defaults(_dev->of_node, false); if (ret < 0) return ret; // Attach the device to the power domain. ret = dev_pm_domain_attach(_dev, true); if (ret) goto out; // Call the driver's probe function. if (drv->probe) { ret = drv->probe(dev); if (ret) dev_pm_domain_detach(_dev, true); }out: if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {// Handle probe deferral and error cases. dev_warn(_dev, "probe deferral not supported\n"); ret = -ENXIO; } return ret;} |
The main logic of this function is as follows:
First, the device pointer passed to the driver
_devis converted toplatform_driverstructure pointer drv, and the device pointer passed to the driver_devis converted toplatform_devicestructure pointer dev.use
of_clk_set_defaults()The function sets the default clock properties of the device node. This function configures the device’s clock based on the attribute information of the device node.Call
dev_pm_domain_attach()Attach the device to a power domain. This function associates the device with the corresponding power domain based on the device’s power management requirements.If the driver’s
probefunction exists, call it to perform the device’s probe operation.drv->probe(dev)Indicates calling the driver’sprobefunction, and passplatform_devicestructure pointerdevas a parameter. If probing fails, it will calldev_pm_domain_detach()detach the device’s power domain.Handle probe deferral and error cases. If the driver sets the
prevent_deferred_probeflag, and the return value is-EPROBE_DEFER, it means the probe is deferred, but the driver does not support deferred probing. In this case, the code prints a warning message “probe deferral not supported” and sets the return value to-ENXIO, indicating that the device does not exist.
Overall, the function’s role is to perform the platform driver’s probe operation, and on the device call the driver’sprobefunction, and handle probe deferral and error cases.

