八、device_add

1.总体框架

linux设备模型:设备device,驱动driver,总线bus。


设备代表物理设备驱动代表了设备操作方法bus则是用来管理和匹配它们


device和driver里面都有一个成员变量bus,表示它们归哪个总线管理;


bus里面则有两个链表,device链表和driver链表


当有新的设备加入的时候,就会将它加入它对应的bus的device链表,然后在它的驱动链表中寻找是否有驱动driver和该device匹配成功,如果匹配成功设备就可以正常使用了,否则,不好意思继续等待。

当有新的驱动加入的时候,就会将它加入它对应的bus的driver链表,后在它的设备链表中寻找是否有设备device和该driver匹配成功,如果成功设备就可以正常使用了。

device_add就是将设备加入到Linux设备模型的关键,它的内部将找到它的bus,然后让它的bus给它找到它的driver,其调用顺序为:

                                                                                 


platform_driver_register(&csid_driver);


driver_sysfs_add



2.1 device_add

[cpp]  view plain  copy
 print ?
  1. int device_add(struct device *dev)  
  2.     dev = get_device(dev);//增加该设备的引用计数  到底还是增加dev->kobj->kref的计数
  3.     if (!dev->p) {  
  4.         error = device_private_init(dev);//初始化设备的私有成员p  
  5.         if (error)  
  6.             goto done;  
  7.     }  
  8.   
  9.     if (dev->init_name) {  
  10.         dev_set_name(dev, "%s", dev->init_name);//初始化设备内部的dev->kobj->name 的名字  
  11.         dev->init_name = NULL;  
  12.     }  
  13.   
  14.     if (!dev_name(dev) && dev->bus && dev->bus->dev_name)       
  15.         dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);//使用bus以及设备id来初始化设备内部kobject名字,一般dev->init_name 
  16.    //设置成功后,dev_name(dev)返回dev->kobj->name,if条件不成立,不执行
  17.     if (!dev_name(dev)) {//获得设备的名字  
  18.         error = -EINVAL;  
  19.         goto name_error;  
  20.     }  
  21.   
  22.     parent = get_device(dev->parent);增加设备父设备并增加父设备引用计数  ,例如:csid的设备节点节v4l-subdev4的父设备是fd8c0000.qcom,msm-cam
  23.     kobj = get_device_parent(dev, parent);  这步非常重要 获取v4l-subdev4设备目录的父目录是video4linux,video4linux的父目录是fd8c0000.qcom,msm-cam

  24.    
  25.     if (kobj)  
  26.         dev->kobj.parent = kobj;//在kobject层实现设备父子关系  
  27.   
  28.     if (parent)  
  29.         set_dev_node(dev, dev_to_node(parent));  //设置该设备节点为-1,一般未注册前默认为-1
  30.     error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);//把内嵌的kobject注册到设备模型中将设备加入到kobject模型中,创建sys相关目 ,目录名字为kobj->name 
  31.       
  32.     /* notify platform of device entry */  
  33.     if (platform_notify)  
  34.         platform_notify(dev);  
  35.   
  36.     error = device_create_file(dev, &uevent_attr);//创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件  在driver_register中已经分析,主要是在/sys/devices/.../中添加dev的uevent属性文件



  37.     if (MAJOR(dev->devt)) {  //如果定义了devt,则产生dev属性,并在/dev目录下产生设备节点文件  
  38.         error = device_create_file(dev, &devt_attr);//创建sys目录下设备的设备号属性,即major和minor /主要是在sys/devices/...中添加dev属性文件
       
  1.         error = device_create_sys_dev_entry(dev);  //在/sys/dev/char/或者/sys/dev/block/创建devt的属性的连接文件,形如10:45,由主设备号和次设备号构成,指向/sys/devices/.../的具体设备目录,该链接文件只具备读属性,显示主设备号:次设备号,如10:45,用户空间udev相应uevent事件时,将根据设备号在/dev下创建节点文件
  2.         devtmpfs_create_node(dev);  
  3.     }  
  4.     error = device_add_class_symlinks(dev);  //创建类符号链接//相互创建dev和class之间的链接文件
   
  r = device_add_attrs(dev); //创建sys目录下设备其他属性文件   //添加设备属性文件
  1.     error = bus_add_device(dev);//将设备加入到管理它的bus总线的设备连表上  创建subsystem链接文件,链接class下的具体的子系统文件夹
  2.     error = dpm_sysfs_add(dev);//电源管理相关  
  3. 添加设备的电源管理属性,截止这里,我们的/sys/devices/.../具体设备目录下至少生成有以下四个属性文件:uevent,dev,subsystem,power,你找到了吗?
  4.     device_pm_add(dev);   //添加设备到激活设备列表中,用于电源管理
  5.   
  6.     /* Notify clients of device addition.  This call must come 
  7.      * after dpm_sysfs_add() and before kobject_uevent(). 
  8.      */  
  9.     if (dev->bus)  
  10.         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);//通知注册监听该总线的设备,有新设备加入  
  11. 执行bus通知链上的注册函数,由设备注册上来
  12.     kobject_uevent(&dev->kobj, KOBJ_ADD);//产生一个内核uevent事件,该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制  
  13. //产生一个KOBJ_ADD的uevent事件,通过netlink机制和用户空间通信,这个driver_register中已经分析过了
  14.     bus_probe_device(dev);//------------给设备探测相应的驱动开始寻找设备所对应的驱动------------  去bus上找dev对应的drv,主要执行__device_attach,主要进行match,sys_add,执行probe函数和绑定等操作
  15.     if (parent)  
  16.         klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children);////把设备添加到父设备的children列表中
  17. 建立设备与总线间的父子关系, 如果设备有父设备,将它加入parent的子设备链中 */  
  18.     if (dev->class) {//如果设备的属于某个设备类,比如Mass storage,HID等等  
  19.         mutex_lock(&dev->class->p->mutex);  如果改dev有所属类,则将dev的添加到类的设备列表里面
  20.         /* tie the class to the device */  
  21.        klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);//将设备挂接在其设备类上面,把设备添加到class的设备链表中,完成关联
  22.   
  23.         /* notify any interfaces that the device is here */  
  24.         list_for_each_entry(class_intf,&dev->class->p->interfaces, node)  
  25.             if (class_intf->add_dev)  
  26.                 class_intf->add_dev(dev, class_intf);//通知有新设备加入  ,//执行改dev的class_intf->add_dev(),这个有个好处,就是只有设备匹配注册成功了,才进行其它的注册工作(如字符设备的注册,生成/dev/***节点文件)以及部分初始化工作。
  27.         mutex_unlock(&dev->class->p->mutex);  
  28.     }  

device_add创建的文件如下:


bus_add_device()

[cpp]  view plain  copy
 
  1. int bus_add_device(struct device *dev)  
  2. {  
  3.          /* 引用计数加一 */  
  4.          struct bus_type *bus =bus_get(dev->bus);  
  5.    
  6.          if (bus) {  
  7.                    /* 创建相应的属性文件 */  
  8.                    error = device_add_attrs(bus,dev);  
  9. /* 在sys/bus/总线类型/devices/dev_name()dev  在devices目录下创建名字为devname(d
  10. 指向sys/devices/相同设备名字的 符号链接*/  
  11.                    error =sysfs_create_link(&bus->p->devices_kset->kobj,
  12.  &dev->kobj,dev_name(dev));  
  13. /* 在sys/devices/设备名字/目录下创建目录名字为 subsystem 并且指向在sys/bus/总线类型/devices/
  14. de符号链接*/
  15.                    error =sysfs_create_link(&dev->kobj,  
  16.                                      &dev->bus->p->subsys.kobj,"subsystem");  
  17.                    /* 把设备加入到总线的设备链中,这步才是重点*/  
  18.                    klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);  
  19.          }  
  20. }  

2.2  bus_probe_device

[cpp]  view plain  copy
 print ?
  1. //为设备找到一个驱动  
  2. void bus_probe_device(struct device *dev)  
  3. {  
  4.     struct bus_type *bus = dev->bus;//获得设备的隶属的总线,该值在设备初始化时设置  
  5.     struct subsys_interface *sif;  
  6.     int ret;  
  7.     if (!bus)  
  8.         return;  
  9.     if (bus->p->drivers_autoprobe) {  
  10.         ret = device_attach(dev);//-------尝试为该设备找一个driver-------  
  11.         WARN_ON(ret < 0);  
  12.     }  
  13.     mutex_lock(&bus->p->mutex);  
  14.     list_for_each_entry(sif, &bus->p->interfaces, node)  
  15.         if (sif->add_dev)  
  16.             sif->add_dev(dev, sif);  
  17.     mutex_unlock(&bus->p->mutex);  
  18. }  

2.3 device_attach

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * device_attach - 尝试为设备寻找到一个驱动 
  3.  *遍历设备隶属的总线上所有的driver,然后调用driver_probe_device匹配设备和驱动,成功就结束循环退出 
  4.  *成功返回值为1,失败为0,-ENODEV表示设备没有被注册 
  5.  */  
  6. int device_attach(struct device *dev)  
  7. {  
  8.     int ret = 0;  
  9.   
  10.     device_lock(dev);  
  11.     if (dev->driver) {//如果设备已经有驱动  ,如果设备已经依附于某个驱动,进行绑定
  12.         if (klist_node_attached(&dev->p->knode_driver)) {  
  13.             ret = 1;  
  14.             goto out_unlock;  
  15.         }  
  16.         ret = device_bind_driver(dev);  如果设备已经依附于某个驱动,进行绑定
  17.         if (ret == 0)  
  18.             ret = 1;  
  19.         else {  
  20.             dev->driver = NULL;  
  21.             ret = 0;  
  22.         }  
  23.     } else {//设备没有驱动  
  24.         pm_runtime_get_noresume(dev);  
  25.         ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);-------遍历总线上的driver链表-------  
  26.         pm_runtime_put_sync(dev);  
  27.     }  
  28. out_unlock:  
  29.     device_unlock(dev);  
  30.     return ret;  
  31. }  

2.4 bus_for_each_drv

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * bus_for_each_drv - driver迭代器 
  3.  * @bus: 设备隶属的总线 
  4.  * @start: 迭代器轮训的起始元素 
  5.  * @data: 传递给回调函数的参数 
  6.  * @fn: 回调函数 
  7.  */  
  8. int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,  
  9.              void *data, int (*fn)(struct device_driver *, void *))  
  10. {  
  11.     struct klist_iter i;  
  12.     struct device_driver *drv;  
  13.     int error = 0;  
  14.   
  15.     if (!bus)  
  16.         return -EINVAL;  
  17.   
  18.     klist_iter_init_node(&bus->p->klist_drivers, &i,start ? &start->p->knode_bus : NULL);  
  19.     while ((drv = next_driver(&i)) && !error)  
  20.         error = fn(drv, data);//-------对于总线中的每个driver调用fn函数进行匹配,fn为__device_attach-------  
  21.     klist_iter_exit(&i);  
  22.     return error;  
  23. }  
__device_attach()->driver_probe_device()->really_probe()->bus->probe()->drv->probe()  总线中定义的probe函数会优先执行,如果总线中没有定义probe才会执行驱动中定义的probe

2.5  __device_attach

[cpp]  view plain  copy
 print ?
  1. static int __device_attach(struct device_driver *drv, void *data)  
  2. {  
  3.     struct device *dev = data;  
  4.   
  5.     if (!driver_match_device(drv, dev))//设备和驱动是否匹配函数,成功就继续下面,否则退出,将调用总线的match函数进行匹配  
  6.         return 0;  
  7.   
  8.     return driver_probe_device(drv, dev);//-------设备和驱动匹配成功,调用probe函数-------  
  9. }-  

2.6 driver_probe_device

[cpp]  view plain  copy
 print ?
  1. int driver_probe_device(struct device_driver *drv, struct device *dev)  
  2. {  
  3.     int ret = 0;  
  4.   
  5.     if (!device_is_registered(dev))//如果设备已经被注册过了,直接退出  
  6.         return -ENODEV;  
  7.   
  8.     pr_debug("bus: '%s': %s: matched device %s with driver %s\n",  
  9.          drv->bus->name, __func__, dev_name(dev), drv->name);  
  10.   
  11.     pm_runtime_get_noresume(dev);  
  12.     pm_runtime_barrier(dev);  
  13.     ret = really_probe(dev, drv);//-------继续调用really_probe函数-------  
  14.     pm_runtime_put_sync(dev);  
  15.     return ret;  
  16. }  

2.7 really_probe

[cpp]  view plain  copy
 print ?
  1. static int really_probe(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     int ret = 0;  
  4.   
  5.     atomic_inc(&probe_count);  
  6.     pr_debug("bus: '%s': %s: probing driver %s with device %s\n",  
  7.          drv->bus->name, __func__, drv->name, dev_name(dev));  
  8.     WARN_ON(!list_empty(&dev->devres_head));  
  9.   
  10.     dev->driver = drv;//匹配好后,将驱动信息记录到设备内部  
  11.     if (driver_sysfs_add(dev)) {  
  12.         printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",  
  13.             __func__, dev_name(dev));  
  14.         goto probe_failed;  
  15.     }  
  16.   
  17.     if (dev->bus->probe) {//如果总线存在probe函数,则调用总线的probe函数  
  18.         ret = dev->bus->probe(dev);  
  19.         if (ret)  
  20.             goto probe_failed;  
  21.     } else if (drv->probe) {  
  22.         ret = drv->probe(dev);//如果总线中没有probe函数,则调用驱动的probe函数  
  23.         if (ret)  
  24.             goto probe_failed;  
  25.     }  
  26.   
  27.     driver_bound(dev);//将设备加入到驱动支持的设备链表中,一个设备需要一个驱动,一个驱动支持多个设备  
  28.     ret = 1;  
  29.     atomic_dec(&probe_count);  
  30.     wake_up(&probe_waitqueue);  
  31.     return ret;  
  32. }  

在驱动或者总线的probe函数中,一般会在/dev/目录先创建相应的设备节点,这样应用程序就可以通过该设备节点来使用设备了。

你可能感兴趣的:(LINUX)