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