Layer 2 device_register of Linux kernel learning (21) Device Model

Source: Internet
Author: User

This article describes the three registration functions and builds the entire device model framework. Of course, this is the key part. Before that, I hope you have understood the data structure of the bus, device, and driver and the relevant data structure in it. If the called function is shown in bold, I will analyze it below.

From: Drivers/base/CORE. c
Int device_register (struct device * Dev)
{
Device_initialize(Dev );
// Initialize the device
ReturnDevice_add(Dev); // Add a device
}

Void device_initialize (struct device * Dev)
{
Dev-> kobj. kset = devices_kset; // set the collection of the device's kobject. The devices_kset is actually at the first layer, sys/devices/
Kobject_init (& Dev-> kobj, & device_ktype); // initialize the kobject of the device
Init_list_head (& Dev-> dma_pools); // initialize the DMA pool of the device for passing big data
Mutex_init (& Dev-> mutex); // initialize the mutex lock
Lockdep_set_novalidate_class (& Dev-> mutex );
Spin_lock_init (& Dev-> devres_lock); // initializes the spin lock, which is used to synchronize the sub-device linked list.
Init_list_head (& Dev-> devres_head); // initialize the sub-device linked list Header
Device_pm_init (Dev );
Set_dev_node (Dev,-1 );
}

Int device_add (struct device * Dev)
{
Struct device * parent = NULL;
Struct class_interface * class_intf;
Int error =-einval;

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 the device
If (error)
Goto done;
}

/*
* For statically allocated devices, which shoshould 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); // you can specify the kobject name.
Dev-> init_name = NULL;
}

If (! Dev_name (Dev )){
Error =-einval;
Goto name_error;
}

Pr_debug ("device: '% s': % s/n", dev_name (Dev), _ FUNC __);

Parent = get_device (Dev-> parent); // Add a reference to the parent device kobject
Setup_parent(Dev, parent); // sets the parent object of the specified device kobject.

/* Use parent numa_node */
If (parent)
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); // Add the device kobject to the parent object Device Model
If (error)
Goto error;

/* Policy platform of device entry */
If (platform_policy)
Platform_policy (Dev );

Error = device_create_file (Dev, & uevent_attr );
If (error)
Goto attrerror;

If (Major (Dev-> devt )){
Error = device_create_file (Dev, & devt_attr );
If (error)
Goto ueventattrerror;

Error = device_create_sys_dev_entry (Dev );
If (error)
Goto devtattrerror;

Devtmpfs_create_node (Dev );
}

Error = device_add_class_symlinks (Dev );
If (error)
Goto symlinkerror;
Error = device_add_attrs (Dev );
If (error)
Goto attrserror;
Error =Bus_add_device(Dev); // Add the device to the bus
If (error)
Goto buserror;
Error = dpm_sysfs_add (Dev );
If (error)
Goto dpmerror;
Device_pm_add (Dev );

/* Sort y clients of device addition. This call must come
* After dpm_sysf_add () and before kobject_uevent ().
*/
If (Dev-> Bus)
Blocking_notifier_call_chain (& Dev-> bus-> P-> bus_notifier,
Bus_policy_add_device, Dev );

Kobject_uevent (& Dev-> kobj, kobj_add );
Bus_probe_device(Dev); // now it is time to find a proper driver for the device on the bus.
If (parent)
Klist_add_tail (& Dev-> P-> knode_parent,
& Parent-> P-> klist_children); // Add the device to the sub-device linked list of the parent device.

If (Dev-> class ){
Mutex_lock (& Dev-> class-> P-> class_mutex );
/* Tie the class to the device */
Klist_add_tail (& Dev-> knode_class,
& Dev-> class-> P-> class_devices );

/* Define y any interfaces that the device is here */
List_for_each_entry (class_intf,
& Dev-> class-> P-> class_interfaces, node)
If (class_intf-> add_dev)
Class_intf-> add_dev (Dev, class_intf );
Mutex_unlock (& Dev-> class-> P-> class_mutex );
}
Done:
Put_device (Dev );
Return Error;
Dpmerror:
Bus_remove_device (Dev );
Buserror:
Device_remove_attrs (Dev );
Attrserror:
Device_remove_class_symlinks (Dev );
Symlinkerror:
If (Major (Dev-> devt ))
Devtmpfs_delete_node (Dev );
If (Major (Dev-> devt ))
Device_remove_sys_dev_entry (Dev );
Devtattrerror:
If (Major (Dev-> devt ))
Device_remove_file (Dev, & devt_attr );
Ueventattrerror:
Device_remove_file (Dev, & uevent_attr );
Attrerror:
Kobject_uevent (& Dev-> kobj, kobj_remove );
Kobject_del (& Dev-> kobj );
Error:
Cleanup_device_parent (Dev );
If (parent)
Put_device (parent );
Name_error:
Kfree (Dev-> P );
Dev-> P = NULL;
Goto done;
}

Int device_private_init (struct device * Dev)
{
Dev-> P = kzarloc (sizeof (* Dev-> P), gfp_kernel );
If (! Dev-> P)
Return-enomem;
Dev-> P-> device = dev; // point to the device itself
Klist_init (& Dev-> P-> klist_children, klist_children_get,
Klist_children_put); // initialize the sub-device linked list of the Private member of the device. There are two functions for increasing or decreasing the sub-device reference count.
Return 0;
}

Static void setup_parent (struct device * Dev, struct device * parent)
{
Struct kobject * kobj;
Kobj = get_device_parent (Dev, parent); // obtain the parent object of the device kobject.
If (kobj)
Dev-> kobj. Parent = kobj;
}

Int bus_add_device (struct device * Dev)
{
Struct bus_type * bus = bus_get (Dev-> Bus );
Int error = 0;

If (bus ){
Pr_debug ("Bus: '% s': add device % s/n", bus-> name, dev_name (Dev ));
Error = device_add_attrs (bus, Dev );
If (error)
Goto out_put;
Error = sysfs_create_link (& bus-> P-> devices_kset-> kobj,
& Dev-> kobj, dev_name (Dev ));
If (error)
Goto out_id;
Error = sysfs_create_link (& Dev-> kobj,
& Dev-> bus-> P-> subsys. kobj, "subsystem ");
If (error)
Goto out_subsys;
Error = make_deprecated_bus_links (Dev );
If (error)
Goto out_deprecated;
Klist_add_tail (& Dev-> P-> knode_bus, & bus-> P-> klist_devices); // Add the device to the device linked list of the bus.
}
Return 0;

Out_deprecated:
Sysfs_remove_link (& Dev-> kobj, "subsystem ");
Out_subsys:
Sysfs_remove_link (& bus-> P-> devices_kset-> kobj, dev_name (Dev ));
Out_id:
Device_remove_attrs (bus, Dev );
Out_put:
Bus_put (Dev-> Bus );
Return Error;
}

VoidBus_probe_device(Struct device * Dev)
{
Struct bus_type * bus = Dev-> bus;
Int ret;

If (Bus & bus-> P-> drivers_autoprobe) {// If You Need to automatically match the driver
Ret =Device_attach(Dev); // find a driver for the device
Warn_on (Ret <0 );
}
}

IntDevice_attach(Struct device * Dev)
{
Int ret = 0;

Device_lock (Dev); // lock the device
If (Dev-> driver) {// if the device has a driver
Ret = device_bind_driver (Dev); // bind the device to the driver.
If (ret = 0)
Ret = 1;
Else {
Dev-> driver = NULL;
Ret = 0;
}
} Else {
Pm_runtime_get_noresume (Dev );
Ret =Bus_for_each_drv(Dev-> bus, null, Dev,_ Device_attach);
// Otherwise, find the driver on the bus to match the device
Pm_runtime_put_sync (Dev );
}
Device_unlock (Dev );
Return ret;
}

IntDevice_bind_driver(Struct device * Dev)
{
Int ret;

Ret = driver_sysfs_add (Dev );
If (! RET)
Driver_bound (Dev); // The driver binds the device.
Return ret;
}

IntBus_for_each_drv(Struct bus_type * bus, struct device_driver * start,
Void * data, INT (* fN) (struct device_driver *, void *))
{
Struct klist_iter I;
Struct device_driver * DRV;
Int error = 0;

If (! Bus)
Return-einval;

Klist_iter_init_node (& bus-> P-> klist_drivers, & I,
Start? & START-> P-> knode_bus: NULL); // initialize the I struct
While (DRV = next_driver (& I ))&&! Error) // traverse the driver on the bus
Error = FN (DRV, data); // match the driver with the device. FN =__ device_attach
Klist_iter_exit (& I );
Return Error;
}

Static int_ Device_attach(Struct device_driver * DRV, void * Data)
{
Struct device * Dev = data;

If (!Driver_match_device(DRV, Dev) // use the match function on the bus to perform low-level matching.
Return 0;

ReturnDriver_probe_device(DRV, Dev); // advanced match
}

Static inline intDriver_match_device(Struct device_driver * DRV,
Struct device * Dev)
{
Return DRV-> bus-> match? DRV-> bus-> match (Dev, DRV): 1;
// No. The match function defined on the bus is called here.
}

IntDriver_probe_device(Struct device_driver * DRV, struct device * Dev)
{
Int ret = 0;

If (! Device_is_registered (Dev) // whether the device is registered
Return-enodev;

Pr_debug ("Bus: '% s': % s: Matched Device % s with driver % s/n ",
DRV-> bus-> name, _ FUNC __, dev_name (Dev), DRV-> name );

Pm_runtime_get_noresume (Dev );
Pm_runtime_barrier (Dev );
Ret =Really_probe(Dev, DRV); // call the real match
Pm_runtime_put_sync (Dev );

Return ret;
}

Static intReally_probe(Struct device * Dev, struct device_driver * DRV)
{
Int ret = 0;

Atomic_inc (& probe_count );
Pr_debug ("Bus: '% s': % s: Probing driver % s with device % s/n ",
DRV-> bus-> name, _ FUNC __, DRV-> name, dev_name (Dev ));
Warn_on (! List_empty (& Dev-> devres_head ));

Dev-> driver = DRV;
If (driver_sysfs_add (Dev )){
Printk (kern_err "% s: driver_sysfs_add (% s) failed/N ",
_ FUNC __, dev_name (Dev ));
Goto probe_failed;
}

If (Dev-> bus-> probe) {// use the probe function defined on the bus to try it.
Ret = Dev-> bus-> probe (Dev );
If (RET)
Goto probe_failed;
} Else if (DRV-> probe) {// if not, try probe on the driver.
Ret = DRV-> probe (Dev );
If (RET)
Goto probe_failed;
}

Driver_bound (Dev); // The driver binds the device.
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;

Probe_failed:
Devres_release_all (Dev );
Driver_sysfs_remove (Dev );
Dev-> driver = NULL;

If (Ret! =-Enodev & RET! =-Enxio ){
/* Driver matched but the probe failed */
Printk (kern_warning
"% 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 );
Wake_up (& probe_waitqueue );
Return ret;
}

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.