Kernel device model

Source: Internet
Author: User

Kernel device model (switch)

 

This article summarizes some content from four aspects:
1. Underlying data structure: kobject, kset.
2. Linux Device Model Hierarchy: bus_type, device, device_driver.
3. Integration: Simple Analysis of PCI Device Driver Model instances and devices and device driver registration source code.
4. Application Analysis of Object-oriented Thinking in the Linux device model.

I. Underlying data structure: kobject, kset
Let's talk about the meaning of the Model:
In general, it is to manage all devices systematically.

There are two levels of implementation:
First, underlying data structures are used to implement basic objects and their hierarchies: kobjects and ksets.
The second is the device model based on the two underlying data structures: Bus, device, and driver.

Kobject

Combined with object-oriented thinking. This kobject belongs to the most basic structure, that is, the highest abstraction layer (a bit like the cobject class in Java ). Any device model, such as bus, device, and driver, belongs to a kobject. In implementation, this derivative relationship is to include a kobject variable in the struct.

The top-level kobject structure provides the most basic functions required by all models:
1. The reference count is used by the kernel to maintain its existence and extinction.
2 sysfs indicates that each object under sys/corresponds to a kobject.
3. hot plugging event processing. Process hot plugging events of devices.

Kobjects corresponds to a set of functions in the kernel, such as application, initialization, addition, registration, counting, and release.
Struct kobject {
Const char * k_name; Name
Char name [kobj_name_len];
Struct kref; count
Struct list_head entry; used to connect to a linked list of similar kobjects
Struct kobject * parent; used to implement layers and point to its parent object.
Struct kset * kset; used to implement the hierarchy, the set to which it belongs
Struct kobj_type * ktype; specifies the type of the object.
Struct dentry * dentry; indicates the directory item in sysfs
Wait_queue_head_t poll;
}; (Linux 2.6.18)

Kset and kobj_type

Kset
It is a collection or container. Implements the object hierarchy. All the parents of a ksets object (kobject) point to the kobj of the ksets.
All objects are connected to the kset
List table. At the same time, subsys is located above the ksets level. subsys has been canceled in the latest kernel because it is essentially a ksets. Kset
Similar to the operation of kobject, the implementation only further calls the corresponding operation of its own kobj. After all, ksets is essentially a kobject.
Struct kset {
Struct subsystem * subsys; there is no subsys concept in the latest kernel. Unified use of ksets
Struct kobj_type * ktype; type.
Struct list_head list; linked list of the same kset
Spinlock_t list_lock;
Struct kobject kobj; its own kobjects
Struct kset_uevent_ops * uevent_ops;
}; (Linux 2.6.18)

Finally, objects belonging to the same set can have a common property: ktype.

Struct kobj_type {
Void (* release) (struct kobject *);
Struct sysfs_ops * sysfs_ops;
Struct attribute ** default_attrs;
};
The so-called attribute is more specific to some key-value pairs. In addition, the show function in sysfs_ops is called by the file system to display the values of each entry attribute under sys.

In this way, kobjects and ksets implement the underlying skeleton of the hierarchy tree.
Further, the device driver model of the upper layer is realized by encapsulating these underlying structures.
Kernel device driver models are divided into three layers: Bus, device, and driver.

Ii. Linux Device Model Hierarchy: bus_type, device, device_driver

The basic relationship is summarized as follows:
The driver core can register multiple types of bus.
Many devices can be mounted under each bus. (Through kset devices)
Many device drivers can be used for each type of bus. (By including a kset drivers )}
Each driver can process a group of devices.

The establishment of this basic relationship stems from the abstraction of various bus, devices, and driving structures in the actual system.

Let's take a look at the definition of the three data structures.

Bus, bus_type.
Struct bus_type {
Const char * Name;

Struct subsystem subsys; // represents itself
Struct kset drivers; // the device driver set of the current bus
Struct kset devices; // a collection of all devices
Struct klist klist_devices;
Struct klist klist_drivers;

Struct bus_attribute * bus_attrs; // bus attribute
Struct device_attribute * dev_attrs; // device attribute
Struct driver_attribute * drv_attrs;

INT (* match) (struct device * Dev, struct device_driver * DRV); // Device Driver matching function
INT (* uevent) (struct device * Dev, char ** envp,
Int num_envp, char * buffer, int buffer_size); // hot plugging event
INT (* probe) (struct device * Dev );
INT (* remove) (struct device * Dev );
Void (* shutdown) (struct device * Dev );
INT (* suspend) (struct device * Dev, pm_message_t State );
INT (* resume) (struct device * Dev );
};
This is the definition of 2.6.18. The source code can describe everything. The following is the definition of the device:

Struct device {

Struct device * parent; // parent device. Generally, a bus corresponds to a device.
Struct kobject kobj; // represents itself
Char bus_id [bus_id_size];
Struct bus_type * bus;/* bus */
Struct device_driver * driver;/* matched driver */

Void * driver_data;/* data private to the driver points to the driver */
Void * platform_data;/* platform specific data, defined by the driver and used */

/// Ignore more fields

};

The device driver definition is as follows:

Struct device_driver {
Const char * Name;
Struct bus_type * bus; // Bus

Struct completion unloaded;
Struct kobject kobj; // represents itself
Struct klist klist_devices; // list of devices
Struct klist_node knode_bus;

Struct module * owner;

INT (* probe) (struct device * Dev );
INT (* remove) (struct device * Dev );
Void (* shutdown) (struct device * Dev );
INT (* suspend) (struct device * Dev, pm_message_t State );
INT (* resume) (struct device * Dev );
};

OK. The basic things are clear. Take a look at the details through the instance of the device model in the PCI driver.

Iii. Integration: Simple Analysis of PCI Device Driver Model instances and device driver registration source code.

First, let's look at the definition of the PCI bus type:
Struct bus_type pci_bus_type = {
. Name = "PCI ",
. Match = pci_bus_match,
. Uevent = pci_uevent,
. Probe = pci_device_probe,
. Remove = pci_device_remove,
. Suspend = pci_device_suspend,
. Shutdown = pci_device_shutdown,
. Resume = pci_device_resume,
. Dev_attrs = pci_dev_attrs,
};

Then there are PCI devices and drivers. The PCI device and the PCI driver do not directly use the device and device_driver. Instead, they are encapsulated, and PCI-specific information is added to form the pci_dev and pci_driver. Of course, the meaning is the same.

Struct pci_dev {
/* ID of the PCI device */
Unsigned int devfn;
Unsigned Short Vendor;
Unsigned short device;
Unsigned short subsystem_vendor;
Unsigned short subsystem_device;
Unsigned int class;
/*...*/

Struct pci_bus * bus; // PCI Bus
Struct pci_driver * driver; // The PCI driver to which the driver belongs.
/*...*/
Struct device dev; // the device itself
/*...*/
};
Many PCI device-specific information, such as interruptions and resources, is omitted here ..

When a PCI device is discovered, the PCI core creates a new struct pci_dev variable in the memory. This PCI device's bus-specific member is
PCI core initialization (devfn, vendor, device, and other members), and the parent of the struct device variable
The variable is set as a PCI bus device (note that the bus not only has a bus_type structure, but also corresponds to a device). The bus variable is set to point
Pci_bus_type structure. The name and bus_id variables are set next, according to the name and ID of the read from PCI device.

After the PCI device structure is initialized, the PCI device is registered to the driver core and calls device_register (& Dev-> Dev );
In the device_register function, kobject is registered to the driver core, and the PCI device is added to the PCI bus device list. hot plugging events are generated.
Kobject is added to the parent linked list, and the sysfs entry is also added.

The discovery of PCI devices is implemented by detecting the PCI space through specific code. The PCI device is automatically generated by the kernel. In this way, when the PCI driver is registered, the PCI device has been registered, and its attributes such as ID information have been initialized.

The last is pci_driver:
Struct pci_driver {
Struct list_head node;
Char * Name; // driver name
Const struct pci_device_id * id_table;/* List of device IDs supported by the driver */
INT (* probe) (struct pci_dev * Dev, const struct pci_device_id * ID);/* New device inserted */
Void (* remove) (struct pci_dev * Dev);/* device removed (null if not a hot-plug capable driver )*/
INT (* suspend) (struct pci_dev * Dev, pm_message_t State);/* Device suincluded */
INT (* resume) (struct pci_dev * Dev);/* Device woken up */
INT (* enable_wake) (struct pci_dev * Dev, pci_power_t state, int enable);/* enable wake event */
Void (* shutdown) (struct pci_dev * Dev );

Struct pci_error_handlers * err_handler;
Struct device_driver driver; // Device Driver
Struct pci_dynids dynids;
};

The definitions of pci_bus, pci_dev, and pci_driver are listed here. They have the same relationship with bus, device, and driver. Pci_bus is an object initialized by the bus_type structure.
Pci_dev is tested by the kernel and registered to the driver core. The initialization and registration of PCI devices are divided into two aspects: one is PCI device information such as ID and resource, and the other is the registration of pci_dev.dev. Call register_device (struct device * Dev.
Pci_driver
It is generally defined by the module and registered to the kernel in the module initialization function. There are also two aspects: one is the PCI-Specific Method in the pci_driver, the supported ID list initialization, and the other is embedded
Register the device_driver and use register_driver (struct device_driver * DRV ).
This is a bit like the relationship between the Child class and the parent class in the object-oriented system. The call of the Child class constructor implies the call of the parent class constructor.

Without the registration of register_device (Dev) and register_driver (DRV), the driver core does not know the existence of the device and driver, and sysfs does not have a related entry.

Last thing, let's look at the code of register_device (Dev) and register_driver (DRV.

Int device_register (struct device * Dev)

{
Device_initialize (Dev );
Return device_add (Dev );
}

Device_register --> device_initialize (Dev); // initialize each field of the device.

Void device_initialize (struct device * Dev)
{
Kobj_set_kset_s (Dev, devices_subsys); // All Dev belong to the set of devices_subsys.
Kobject_init (& Dev-> kobj); // initial kobj
Klist_init (& Dev-> klist_children, klist_children_get,
Klist_children_put );
Init_list_head (& Dev-> dma_pools );
Init_list_head (& Dev-> node );
Init_mutex (& Dev-> SEM );
Device_init_wakeup (Dev, 0 );
}

Device_register --> device_add (Dev );

Int device_add (struct device * Dev) // MAIN PROCESS
{
Dev = get_device (Dev );
Parent = get_device (Dev-> parent );
Kobject_set_name (& Dev-> kobj, "% s", Dev-> bus_id );
Dev-> kobj. Parent = & parent-> kobj;
Kobject_add (& Dev-> kobj); // Add your own kobject to the hierarchy and create sysfs entry.

// Set uevent_attr:

Dev-> uevent_attr.attr.name = "uevent ";
Dev-> uevent_attr.attr.mode = s_iwusr;

If (Dev-> driver)
Dev-> uevent_attr.attr.owner = Dev-> driver-> owner;
Dev-> uevent_attr.store = store_uevent;
Device_create_file (Dev, & Dev-> uevent_attr );

// Create the sysfs entry that displays the device number, that is, the "Dev" file under the current device entry displays the Master/Slave Device Number of the device.

If (Major (Dev-> devt )){
ATTR-> ATTR. Name = "Dev ";
ATTR-> ATTR. mode = s_irugo;
If (Dev-> driver)
ATTR-> ATTR. Owner = Dev-> driver-> owner;
ATTR-> show = show_dev;
Error = device_create_file (Dev, ATTR );
}

// Establish the sysfs symbolic connection of the class
If (Dev-> class ){
Sysfs_create_link (& Dev-> kobj, & Dev-> class-> subsys. kset. kobj, "subsystem ");
Sysfs_create_link (& Dev-> class-> subsys. kset. kobj, & Dev-> kobj, Dev-> bus_id );}
Sysfs_create_link (& Dev-> kobj, & Dev-> parent-> kobj, "device ");
Class_name = make_class_name (Dev-> class-> name, & Dev-> kobj );
Sysfs_create_link (& Dev-> parent-> kobj, & Dev-> kobj, class_name );
}

Error = bus_add_device (Dev); // Add some bus-related sysfs symbolic connections.

/* Set the environment variable, and then call call_usermodehelper (argv [0], argv, envp, 0); this causes hot plugging event user space script execution. */

Kobject_uevent (& Dev-> kobj, kobj_add );

Bus_attach_device (Dev );
/* If Dev-> driver already exists, call device_bind_driver (Dev); to bind it; otherwise, traverse Dev-> bus.
Drivers list, call Dev-> bus. Match (Dev, DRV) to check whether a driver matches the dev. If it matches, it is bound. */

} OK. The above is the main process ..

The following is the register_driver (DRV) function:

Int driver_register (struct device_driver * DRV)
{
If (DRV-> bus-> probe & DRV-> probe) |
(DRV-> bus-> remove & DRV-> remove) |
(DRV-> bus-> shutdown & DRV-> shutdown )){
Printk (kern_warning "Driver'' % s' needs updating-Please use bus_type methods/N ", DRV-> name );
}
Klist_init (& DRV-> klist_devices, klist_devices_get, klist_devices_put );
Init_completion (& DRV-> unloaded );
Return bus_add_driver (DRV );
}

Driver_register (DRV); --> bus_add_driver (DRV );

Int bus_add_driver (struct device_driver * DRV)
{
Struct bus_type * bus = get_bus (DRV-> Bus );

Error = kobject_set_name (& DRV-> kobj, "% s", DRV-> name );
DRV-> kobj. kset = & bus-> drivers; // driver set of the bus
Error = kobject_register (& DRV-> kobj); // register your own kobject

Driver_attach (DRV); // Add the driver to the bus
Klist_add_tail (& DRV-> knode_bus, & bus-> klist_drivers );
Module_add_driver (DRV-> owner, DRV );

Driver_add_attrs (bus, DRV );
Add_bind_files (DRV );

}

Driver_register (DRV); --> bus_add_driver (DRV); --> driver_attach (DRV );

Void driver_attach (struct device_driver * DRV)
{
Bus_for_each_dev (DRV-> bus, null, DRV, _ driver_attach );
}

Call _ driver_attach (Dev, DRV) for each device on the bus.
Driver_probe_device (DRV, Dev );

Driver_register (DRV); --> bus_add_driver (DRV); --> driver_attach (DRV );
-- >__ Driver_attach (Dev, DRV); --> driver_probe_device (DRV, Dev );

Int driver_probe_device (struct device_driver * DRV, struct device * Dev)
{
If (DRV-> bus-> match &&! DRV-> bus-> match (Dev, DRV ))
Goto done; // give priority to calling the bus to provide matching methods

Dev-> driver = DRV;
If (Dev-> bus-> probe ){
Ret = Dev-> bus-> probe (Dev); // bus Detection Method
}
Else if (DRV-> probe)
{
Ret = DRV-> probe (Dev); // use the dev-> driver test method.
}
Device_bind_driver (Dev);/* If the test succeeds, the device is bound to the driver. Add Dev to the DRV device list and establish a symbolic connection between the driver and the device in the SYS/portal */

Goto done;

Done:
Return ret;
}

Messy. The level of the main line or model. The attributes, hot plugging, and SYS entries in kobject and kset details are not detailed. Perhaps it is more important to understand the overall and design ideas. People have limited energy.

4. Application Analysis of Object-oriented Thinking in the Linux device model.

Through the device model, we can see that object-oriented programming is implemented in C language. For example, the simplest Data encapsulation method. Here we will show more about the inheritance implementation. For example, pci_driver,
Its parent class is device_driver, And the last layer is a kobject. In C ++, if a parent class is inherited, the Child class contains an instance of the parent class. The kernel also uses packets
Contains a parent class entity to implement this derivative relationship. Therefore, a pci_driver must contain a device_driver. Similarly
Must contain a kobject.
As mentioned above, the process of registering a model is similar to calling constructors in object-oriented programming. Child classes need to call the parent class constructor to complete their own construction. Let's take a look at the process of registering a pci_driver:
Pci_register_driver (struct pci_driver * driver)
--> Driver_register (& DRV-> driver );
--> Kobject_register (& DRV-> kobj );
What Is Inheritance in OO ??

The concept of polymorphism (virtual function) can also be found in the device model source code. Isn't it strange to see that pci_driver and device_driver provide methods of the same name ?? They are different
Is the parameter. In pci_driver, the parameter of the method is pci_device * Dev, while that of the device_driver method is device *
Dev. This arrangement is intended! See how platform_driver works.

The most typical examples are platform_driver and device_driver.
Struct platform_driver {
INT (* probe) (struct platform_device *);
INT (* remove) (struct platform_device *);
Void (* shutdown) (struct platform_device *);
INT (* suspend) (struct platform_device *, pm_message_t State );
INT (* resume) (struct platform_device *);
Struct device_driver driver;
};
This is obviously simpler than pci_driver. In addition to one device_driver, platform_driver contains five methods with the same name as device_driver.
The process of registering a platform_driver:
Int platform_driver_register (struct platform_driver * DRV)
{
DRV-> driver. Bus = & platform_bus_type;
If (DRV-> probe)
DRV-> driver. Probe = platform_drv_probe;
If (DRV-> remove)
DRV-> driver. Remove = platform_drv_remove;
If (DRV-> shutdown)
DRV-> driver. Shutdown = platform_drv_shutdown;
If (DRV-> suspend)
DRV-> driver. Suspend = platform_drv_suspend;
If (DRV-> resume)
DRV-> driver. Resume = platform_drv_resume;
Return driver_register (& DRV-> driver );
}

The function pointer of device_driver included in platform_driver is set here. Look at platform_drv_probe in these functions.
Static int platform_drv_probe (struct device * _ Dev)
{
Struct platform_driver * DRV = to_platform_driver (_ Dev-> driver );
Struct platform_device * Dev = to_platform_device (_ Dev );

Return DRV-> probe (Dev );
}

Here two pointer types are converted, and then the probe function provided by platform_driver is called.
Consider the registration process of platform_driver. The registration process for each driver is the same. As previously analyzed, after entering driver_register, the device driver
The probe at the device_driver layer will be called to detect devices. This function completes type conversion as indicated by the source code above and calls its subclass platform_driver.

The probe function of the layer to complete specific functions. From the device_driver layer, the same function call is performed by sub-classes to complete different specific functions. Isn't this a multi-state idea ??

The basic idea of using C in the Linux Device Model to implement object-oriented elements (encapsulation, inheritance, and polymorphism) is analyzed. Using C to do more work, but it is more flexible. No wonder Linus shot C ++.
"The only way to use excellent, efficient, system-level, and portable C ++ is to use all the features of C. "

PS:
Linux Kernel Modules:

Http://www.ibm.com/developerworks/cn/linux/l-lkm/

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.