Intuitive understanding of device-driven models

In the early days of Linux, a driver corresponds to a device, which corresponds to a hardware address. When there are two identical devices, it is obviously unreasonable to write two drivers. It should be from Linux 2.5, the device-bus-driver model was introduced. The main structure of the device driver model is divided into kset, kobject, and ktype.

Kset is a collection of kobjects of the same type. It can be said to be a container.

Kobject is a base class for three types of objects: bus, driver, and device, and implements a common interface.

Ktype, records some properties of the kobject object.

The core of the device-driven model is kobject, which is to manage the increasing number of devices and make the device have a specific unified interface at the bottom layer. It is closely linked with the sysfs file system. Each registered kobject corresponds to a directory in the sysfs file system. For intuitive management, unified storage path, use kset. However, these directories only have no meaning. These two structures can only show the hierarchical relationship of the devices. Therefore, they are not used separately and will be embedded in a larger structure. (If you want to see them in the driver directory, you can see Various drivers on the bus, and can see various devices hanging on the bus under the device directory, the kobject will be embedded in the description of the device and the structure of the drive, so that every time you register a device or driver, will be in the sys There is a description under the directory)

Put a classic picture:

In fact, this figure also misses a ktype, kobject should contain a ktype.

The purpose of the Linux device model is to establish a unified device model for the kernel and thus have a general abstract description of the system architecture.

We can first look at the next small test program:

As you can see, we use the kobject, kset, and ktype structures to create (via kset_create_and_add and kobject_init_and_add functions) subdirectories (kobject_test) and properties files under the sysfs virtual file system. Kset and kobject can create a directory, but kset directory stores kobject directory, kobject stores property files (you can read and write property files, as shown in the figure name property file, and the kobject directory can also store kobject directory, only Need parent to point to it).

This little program did not understand? It does not matter, first look at the following analysis: (see the end of the article and then look back at this program, you will have a more intuitive understanding)

Under the analysis of the Linux kernel source code, we can look at the members of the three structures:

In fact, when it comes to the device driver model, it is easy to think of the platform. Let's analyze this in detail:

In init/main.c:

This is the driver_init function:

Let's look at the devices_init function:

This calls kset_create_and_add to create a kset and return to devices_kset. Note that the devices_kset can be said to be one of the largest bosses under /sys. All the physical devices will be managed in the device directory. The /sys/device/ directory is the kernel to the system. The hierarchical expression model of all devices in the system saves all system equipment.

Then call the kobject_create_and_add function to create the dev directory in the /sys/ directory. The /sys/dev directory maintains a symbolic link that links the major and minor numbers (major:minor) of the character device and the block device to the real device (/sys/devices). Files, applications can access actual devices by reading, writing, and controlling these files.

Finally, using dev_kobj as the parent node, create the block and char directories in the /sys/dev/ directory.

Here we first look at the kobject_create_and_add function, and then analyze the kset_create_and_add function:

In fact, there is no function inside, first create a kobject, initialize it, add, nothing to say.

In addition to the kobject_create_and_add function, there is a similar function: kobject_init_and_add.

Kobject_init_and_add passes in a kobject pointer and a kobj_type pointer, then initializes

Kobject_create_and_add creates a kobject variable and returns its pointer without passing in the kobj_type pointer

Kobject is also used in the kset_create_and_add function, so we will now analyze the kset_create_and_add function:

Inside is the specific creation and registration of kset.

Let's talk about creating a function:

Static struct kset *kset_create(const char *name,

Const struct kset_uevent_ops *uevent_ops,

Struct kobject *parent_kobj)

{

Struct kset *kset;

Int retval;

Kset = kzalloc(sizeof(*kset), GFP_KERNEL);//Assign kset space

If (!kset)

Return NULL;//fail to return

Retval = kobject_set_name(&kset->kobj, "%s", name);//Set the kset name, which is the name of the embedded kobject

If (retval) {

Kfree(kset);

Return NULL;

}

Kset->uevent_ops = uevent_ops;//kset property operations

Kset->kobj.parent = parent_kobj; //Set its parent

Kset->kobj.ktype = &kset_ktype;//ktype is specified as kset_ktype

Kset->kobj.kset = NULL;

Return kset;

}

It can be seen that the contents of the kset_create function are:

1) call the kobject_set_name function to set the name of the kobject 2) set the uvent_ops of the kobject, the parent is the passed parameter uevent_ops, parent_kobj 3) set the kobject of the kobject to the system-defined ktype variable 4) set the kobject of the kobject to NULL, meaning The kset to which the kobject belongs is the kset itself because the kset structure contains a kobject member.

One thing that needs attention here is the ktype structure, kset_ktype:

This is filled with a release function, each kobject must have a release function, and this kobject must remain until the release function is called. If this condition cannot be satisfied, this code is defective. Note that if you forget to provide the release function, the kernel will warn you; do not attempt to provide an empty release function to eliminate this warning, and you will receive ruthless derision from the kobject maintainer.

As for kobj_sysfs_ops, it is about the set of operations related to read and write operations:

When reading a file, the callback function to .show is called. When writing a file, callbacks to .store are called.

After reading the create function, the next step is to register the function:

The kset_init function mainly initializes the kset, which will initialize the reference counter (ie, kobj->kref) to 1 (cannot be released before the counter reference count reaches 0). Then initialize the entry list node, which is used to compose a linked list (INIT_LIST_HEAD(&kobj->entry)) with the list member of the kset to which it belongs, and the assignment of some parameters. Finally, it also initializes the list with the list member as the head node, which forms a linked list with the entry member of the child kobject (INIT_LIST_HEAD(&k->list)).

The kobject_add_internal function is the key kobject function:

Static int kobject_add_internal(struct kobject *kobj)

{

Int error = 0;

Struct kobject *parent;

If (!kobj)

Return -ENOENT;

If (!kobj->name || !kobj->name[0]) {// If kobject's name is empty. Exit

WARN(1, "kobject: (%p): attempted to be registered with empty "

"name!", kobj);

Return -EINVAL;

}

Parent = kobject_get(kobj->parent);// If kobj-parent is true, increase the kobj->kref count, ie the reference count of the parent node

/* 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);// If the parent parent is NULL then use kobj->kset->kobj as its parent and increase its reference count

Kobj_kset_join(kobj) ;// Add the entry member of kobj to the end of kobj->kset>list, the current level is kobj->kset->list pointing to kobj->entry

Kobj->parent = parent;

}

/* Deleted some debugging content */

Error = create_dir(kobj) ;// use kobj to create directory and properties files, which will determine if the parent is NULL then created under sysfs_root_kn

If (error) {

/* Deleted some of the content */

} else

Kobj->state_in_sysfs = 1;//If created successfully. Build state_in_sysfs to 1. Indicates that the object is already in sysfs

Return error;

}

The contents of the kobject_add_internal function are written in the comments and can be summarized as:

1) If the parent member of the kobject is NULL, it points to the kobject member of the kset. 2) If the kobject member's kset member is not NULL, it will call the kobj_kset_join function to add the kobject's entry member to the kset's list list. 3) Finally, call the create_dir function to create the sys directory.

The last call in the register function is the kobject_uevent function. It should be about the hot swap mechanism. This is not what we are concerned about now.

Well, after the above toss, will create a devices directory in / sys / directory.

Next, go back to the devices_init function that starts at the beginning of the article:

We analyzed the devices_init function before. In fact, the next few functions are the same. Create directories in the /sys/ directory.

Just remember device_kset corresponding to /sys/devices directory bus_kset corresponding to /sys/bus directory devices_kset corresponding to /sys/devices directory system_kset corresponding to /sys/devices/system directory class_kset corresponding to /sys/class directory firmware_kobj corresponding to /sys/firmware directory hypervisor_kobj corresponding /sys/hypervisor directory

Next look at the platform_bus_init function

That is the platform bus we used before! !

In the driver/base/platform.c file:

Here, device_register is to create platform in / sys / device / directory

In fact, it also contains two functions, an initialization, an addition:

Void device_initialize(struct device *dev)

{

Dev->kobj.kset = devices_kset ;/ / set the device kobject belonging to the collection, devices_kset that corresponds to / sys / devices /

Kobject_init(&dev->kobj, &device_ktype);//Initialize device kobject

INIT_LIST_HEAD(&dev->dma_pools);//Initialize device's DMA pool for passing big data

Mutex_init(&dev->mutex);

Lockdep_set_novalidate_class(&dev->mutex);

Spin_lock_init(&dev->devres_lock);//initial spin lock, used to synchronize child device linked list

INIT_LIST_HEAD(&dev->devres_head);//Initialize child device chain header

Device_pm_init(dev);

Set_dev_node(dev, -1);#ifdef CONFIG_GENERIC_MSI_IRQ

INIT_LIST_HEAD(&dev->msi_list);#endif

}

Comments are written, look at the device_add function:

Int device_add(struct device *dev)

{

Struct device *parent = NULL;

Struct kobject *kobj;

Struct class_interface *class_intf;

Int error = -EINVAL;

Struct kobject *glue_dir = NULL;

Dev = get_device (dev) ;/ / increase the reference count of the device's kobject

If (!dev)

Goto done;

If (!dev->p) {

Error = device_private_init(dev);//initialize the private member of dev, and its linked list operation function

If (error)

Goto done;

}

If (dev->init_name) {// save the device name, use dev_name function to get later

Dev_set_name(dev, "%s", dev->init_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 (!dev_name(dev)) {

Error = -EINVAL;

Goto name_error;

}

Pr_debug("device: '%s': %s", dev_name(dev), __func__);

Parent = get_device(dev->parent);//Return parent node, increase parent node reference count, if no NULL is returned

Kobj = get_device_parent(dev, parent);// above devices reset dev->kobj.parent

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));

/* first, register with generic layer. */

/* we require the name to be set before, and pass NULL */

Error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);//Set dev->kobj's name and parent object, and create the corresponding directory

If (error) {

Glue_dir = get_glue_dir(dev);

Goto Error;

}

/* notify platform of device entry */

If (platform_notify)

Platform_notify(dev);

Error = device_create_file(dev, &dev_attr_uevent);//create uevent property file

If (error)

Goto attrError;

Error = device_add_class_symlinks(dev);

If (error)

Goto SymlinkError;

Error = device_add_attrs(dev);

If (error)

Goto AttrsError;

Error = bus_add_device(dev);

If (error)

Goto BusError;

Error = dpm_sysfs_add(dev);

If (error)

Goto DPMError;

Device_pm_add(dev);

If (MAJOR(dev->devt)) {

Error = device_create_file(dev, &dev_attr_dev);//Generate dev attribute file under sys

If (error)

Goto DevAttrError;

Error = device_create_sys_dev_entry(dev);//Create a soft link to the device in the /sys/dev directory

If (error)

Goto SysEntryError;

Devtmpfs_create_node(dev);

}

/* Notify clients of device addition. This call must come

* after dpm_sysfs_add() and before kobject_uevent().

*/

If (dev->bus)

Blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

BUS_NOTIFY_ADD_DEVICE, dev);

Kobject_uevent(&dev->kobj, KOBJ_ADD) ;// issue KOBJ_ADD event to user space

Bus_probe_device(dev);//Check if there is a suitable device in the driver to match. Now only the device is added, the driver is not loaded, so no match is made.

If (parent)

Klist_add_tail(&dev->p->knode_parent,

&parent->p->klist_children);//links the device's node to its parent's linked list

If (dev->class) {

Mutex_lock(&dev->class->p->mutex);

/* tie the class to the device */

Klist_add_tail(&dev->knode_class,

&dev->class->p->klist_devices);

/* 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);

Mutex_unlock(&dev->class->p->mutex);

}

/* Omit some of the error content */

}

The device_add function is more important, the comments are basically written, can be summarized as:

1) Increase the kobj->kref count 2) Initialize the private member of dev 3) Set the device name 4) Increase the parent node reference count 5) Add dev->kobj to the directory corresponding to dev->kobj.parent 6) dev-> Create a properties file under kobj 7) Create a soft link to the device in the /sys/dev directory 8) Drive detection

Among them, the driver detection function: bus_probe_device can Baidu.

Finally, we then look at bus_register(&platform_bus_type);

Today's space is a bit long, the function can write something important:

emphasize again:

Priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype;

This sets the kset and ktype to which it belongs. The ktype structure contains the sysfs_ops structure, which reads and writes files:

Finally, the bus_register function also calls the kset_create_and_add function to create the devices and drivers directory under the /sys/platform/ directory, which stores the devices and drivers registered under our platform platform.

Well, this time, we'll come back to it again:

There may also be a deeper kset under kset

Kset contains one or more kobjects for easy management

Kobject does not necessarily need kset

The kobject has a properties file that provides the user layer with an interface to represent and manipulate the properties of the kobject

There are some symlink files under kobject pointing to other kobject

Is there a more intuitive understanding of the device driver model now? Looking back at the small program at the beginning of the article, isn't it easy to understand?

Protection Relay

Protection Relay,Protection Relay For Medium-Voltage Applications,Earth Leakage Relay,Smart Motor Protector

Jiangsu Acrel Electrical Manufacturing Co., LTD. , https://www.acrel.com.pk