Linux 设备总线驱动模型

2019-07-13 07:24发布

  尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要。
    Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。        在内核里,有各种各样的总线,如 usb_bus_type、spi_bus_type、pci_bus_type、platform_bus_type、i2c_bus_type 等,内核通过总线将设备与驱动分离。此文,基于 Linux2.6.32.2 简单分析设备驱动模型,以后看具体的总线设备模型时会更加清晰。    设备模型是层次的结构,层次的每一个节点都是通过kobject实现的。在文件上则体现在sysfs文件系统。    关于kobkect,前面的文章已经分析过了,如果不清楚请移步 http://blog.csdn.net/qq_30736309/article/details/79570950
    kobject 结构可能的层次结构如图:
    关于 uevet mdev 前面也说过了,请参考 http://blog.csdn.net/lizuobin2/article/details/51534385    对于整个 设备总线驱动模型 的样子,大概如下图吧,也并不复杂。简单来说,bus 负责维护 注册进来的devcie 与 driver ,每注册进来一个device 或者 driver 都会调用 Bus->match 函数 将device 与 driver 进行配对,并将它们加入链表,如果配对成功,调用Bus->probe或者driver->probe函数, 调用 kobject_uevent 函数设置环境变量,mdev进行创建设备节点等操作。后面,我们从 Bus driver 到 device三个部分进行详细的分析。
一、总线
    内核通过 bus_register 进行 Bus  注册,那么它注册到了哪里,“根”在哪? 答案是 bus_kest [cpp] view plain copy
  1. int __init buses_init(void)  
  2. {  
  3.     // /sys/bus 目录 这里创建的  
  4.     bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);  
  5.     if (!bus_kset)  
  6.         return -ENOMEM;  
  7.     return 0;  
  8. }  
    bus 的类型为 bus_type[cpp] view plain copy
  1. struct bus_type {  
  2.     const char      *name;  
  3.     struct bus_attribute    *bus_attrs;  
  4.     struct device_attribute *dev_attrs;  
  5.     struct driver_attribute *drv_attrs;  
  6.   
  7.     int (*match)(struct device *dev, struct device_driver *drv);  
  8.     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
  9.     int (*probe)(struct device *dev);  
  10.     int (*remove)(struct device *dev);  
  11.     void (*shutdown)(struct device *dev);  
  12.   
  13.     int (*suspend)(struct device *dev, pm_message_t state);  
  14.     int (*resume)(struct device *dev);  
  15.   
  16.     const struct dev_pm_ops *pm;  
  17.   
  18.     struct bus_type_private *p;  
  19. };  
[cpp] view plain copy
  1. struct bus_type_private {  
  2.     struct kset subsys;  
  3.     struct kset *drivers_kset;  
  4.     struct kset *devices_kset;  
  5.     struct klist klist_devices;  
  6.     struct klist klist_drivers;  
  7.     struct blocking_notifier_head bus_notifier;  
  8.     unsigned int drivers_autoprobe:1;  
  9.     struct bus_type *bus;  
  10. };  
[cpp] view plain copy
  1. int bus_register(struct bus_type *bus)  
  2. {  
  3.     int retval;  
  4.     struct bus_type_private *priv;  
  5.   
  6.     priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);  
  7. /* 1. bus 与 prv 相互建立联系 */  
  8.     // 私有数据 .bus ->  bus 本身  
  9.     priv->bus = bus;  
  10.     // bus->p 指向 priv  
  11.     bus->p = priv;  
  12.     // 内核通知链  
  13.     BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);  
  14.   
  15. /* 设置 bus->prv->subsys->kobj */  
  16.     // 设置 priv->subsys.kobj.name = bus->name  对应于/sys/ 目录下的目录名  
  17.     retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  
  18.     // 所有的 priv->subsys.kobj.kset 指向 bus_kse 对应于图中④与六的关系  
  19.     priv->subsys.kobj.kset = bus_kset;  
  20.     // 所有的priv->subsys.kobj.ktype 等于 bus_ktype  
  21.     priv->subsys.kobj.ktype = &bus_ktype;  
  22.       
  23.     priv->drivers_autoprobe = 1;  
  24.       
  25. /* 注册 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */    
  26.     // 注册 priv->subsys ,由于 priv->subsys.kobj.kset = bus_kset,所以会在 /sys/bus/目录下创建 目录 如/sys/bus/plateform  
  27.     retval = kset_register(&priv->subsys);  
  28.     // sysfs_create_file(&bus->p->subsys.kobj, &bus_attr_uevent->attr);  
  29.     retval = bus_create_file(bus, &bus_attr_uevent);  
  30.   
  31.     // 由于 priv->subsys.kobj.kset = bus_kset ,因此会创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/devices  
  32.     priv->devices_kset = kset_create_and_add("devices", NULL,  
  33.                          &priv->subsys.kobj);  
  34.     // 同理 创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/drivers  
  35.     priv->drivers_kset = kset_create_and_add("drivers", NULL,  
  36.                          &priv->subsys.kobj);  
  37.     // 初始化 klist_devices 并设置get put 函数  初始化 klist_drivers 不知为何没有get put ?  
  38.     klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  
  39.     klist_init(&priv->klist_drivers, NULL, NULL);  
  40.   
  41.     retval = add_probe_files(bus);  // static inline int add_probe_files(struct bus_type *bus) { return 0; }  
  42.     // 添加 bus->attrs 属性文件  
  43.     retval = bus_add_attrs(bus);  
  44.   
  45.     return 0;  
  46.   
  47. }  

目前,能通过 bus_register 函数处理的工作有:    1、将 Bus 与 priv 相互建立联系
    2、BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
    3、设置 bus->priv->subsys(kset).kobj 的名字为 bus->name
    4、设置 bus->priv->subsys(kset).kobj.kset 指向 bus_kset
    5、设置 bus->priv->subsys(kset).kobj.ktype 为 bus_ktype ,提供 show store 函数
    6、设置 bus->priv->drivers_autoprobe = 1;
    7、注册 bus->priv->subsys(kset)   对应于图中④与⑥的关系
    由于4,且没有指定bus->priv->subsys(kset).kobj.Parent,会将 bus_kest.kobj 设置为 bus->priv->subsys(kset).kobj.Parent 因此,会将bus->priv->subsys(kset).kobj.entry 加入 bus_kest 链表,且会在/sys/bus目录下创建相应的总线目录/sys/bus/$(bus->name),例如 /sys/bus/platform 
    8、创建 bus_attr_uevent->attr 属性文件
    9、创建并注册 devices_kset ,devices_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 device ,因此会创建 /sys/bus/$(bus->name)/devices
    10、创建并注册 drivers_kset ,drivers_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 drivers ,因此会创建 /sys/bus/$(bus->name)/drivers
    11、初始化 bus->priv->klist_devices 链表
    12、初始化 bus->priv->klist_drivers 链表
    13、创建 bus->bus_attrs 属性文件

下面来看个例子 ,修改自LDD3 。基于Linux 2.6.32.2 内核
[cpp] view plain copy
  1. /* 
  2.  * Definitions for the virtual LDD bus. 
  3.  * 
  4.  * lddbus.h 
  5.  */  
  6.   
  7. extern struct device ldd_bus;  
  8. extern struct bus_type ldd_bus_type;  
  9.   
  10.   
  11. /* 
  12.  * The LDD driver type. 
  13.  */  
  14.   
  15. struct ldd_driver {  
  16.     char *version;  
  17.     struct module *module;  
  18.     struct device_driver driver;  
  19.     struct driver_attribute version_attr;  
  20. };  
  21.   
  22. #define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);  
  23.   
  24. /* 
  25.  * A device type for things "plugged" into the LDD bus. 
  26.  */  
  27.   
  28. struct ldd_device {  
  29.     char *name;  
  30.     struct ldd_driver *driver;  
  31.     struct device dev;  
  32. };  
  33.   
  34. #define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);  
  35.   
  36. extern int register_ldd_device(struct ldd_device *);  
  37. extern void unregister_ldd_device(struct ldd_device *);  
  38. extern int register_ldd_driver(struct ldd_driver *);  
  39. extern void unregister_ldd_driver(struct ldd_driver *);  
[cpp] view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include "lddbus.h"  
  7.   
  8. MODULE_AUTHOR("Jonathan Corbet");  
  9. MODULE_LICENSE("Dual BSD/GPL");  
  10. static char *Version = "$Revision: 1.9 $";  
  11.   
  12. //--------------------------------- bus ----------------------------------------  
  13.   
  14. static int ldd_match(struct device *dev, struct device_driver *drv)  
  15. {  
  16.     struct ldd_device *pdev = to_ldd_device(dev);  
  17.       
  18.     return !strncmp(pdev->name, drv->name, strlen(drv->name));  
  19. }  
  20.   
  21. struct bus_type ldd_bus_type = {  
  22.     .name = "ldd",  
  23.     .match = ldd_match,  
  24. };  
  25.   
  26. //--------------------------------- device --------------------------------------  
  27.   
  28. static ssize_t show_bus_version(struct bus_type *bus, char *buf)  
  29. {  
  30.     return snprintf(buf, strlen(Version), "%s ", Version);  
  31. }  
  32.   
  33. static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);  
  34.   
  35. // parent device  
  36. static void ldd_bus_release(struct device *dev)  
  37. {  
  38.     printk(KERN_DEBUG "lddbus release ");  
  39. }  
  40. static void ldd_dev_release(struct device *dev){ }  
  41.   
  42. struct device ldd_bus = {  
  43.     .init_name   = "ldd0","white-space:pre">  // ldd0 就是总线的名字,这里改成 ldd_bus 更恰当  
  44.     .release  = ldd_bus_release  
  45. };  
  46.   
  47. int register_ldd_device(struct ldd_device *ldddev)  
  48. {  
  49.       
  50.     ldddev->dev.bus = &ldd_bus_type;  
  51.     ldddev->dev.parent = &ldd_bus;  
  52.     ldddev->dev.release = ldd_dev_release;  
  53.     return device_register(&ldddev->dev);  
  54. }  
  55. EXPORT_SYMBOL(register_ldd_device);  
  56.   
  57. void unregister_ldd_device(struct ldd_device *ldddev)  
  58. {  
  59.     device_unregister(&ldddev->dev);  
  60. }  
  61. EXPORT_SYMBOL(unregister_ldd_device);  
  62.   
  63. //--------------------------------- driver --------------------------------------  
  64.   
  65. static ssize_t show_version(struct device_driver *driver, char *buf)  
  66. {  
  67.     struct ldd_driver *ldriver = to_ldd_driver(driver);  
  68.   
  69.     sprintf(buf, "%s ", ldriver->version);  
  70.     return strlen(buf);  
  71. }  
  72.           
  73. int register_ldd_driver(struct ldd_driver *driver)  
  74. {  
  75.     int ret;  
  76.       
  77.     driver->driver.bus = &ldd_bus_type;  
  78.     ret = driver_register(&driver->driver);  
  79.     if (ret)  
  80.         return ret;  
  81.     driver->version_attr.attr.name = "version";  
  82.     driver->version_attr.attr.owner = driver->module;  
  83.     driver->version_attr.attr.mode = S_IRUGO;  
  84.     driver->version_attr.show = show_version;  
  85.     driver->version_attr.store = NULL;  
  86.     return driver_create_file(&driver->driver, &driver->version_attr);  
  87. }  
  88.   
  89. void unregister_ldd_driver(struct ldd_driver *driver)  
  90. {  
  91.     driver_unregister(&driver->driver);  
  92. }  
  93. EXPORT_SYMBOL(register_ldd_driver);  
  94. EXPORT_SYMBOL(unregister_ldd_driver);  
  95.   
  96. //--------------------------------- bus ----------------------------------------  
  97.   
  98. static int __init ldd_bus_init(void)  
  99. {  
  100.     int ret;  
  101.     device_register(&ldd_bus);  
  102.     ret = bus_register(&ldd_bus_type);  
  103.     if (ret)  
  104.         return ret;  
  105.     if (bus_create_file(&ldd_bus_type, &bus_attr_version))  
  106.         printk(KERN_NOTICE "Unable to create version attribute ");  
  107.   
  108.     return ret;  
  109. }  
  110.   
  111. static void ldd_bus_exit(void)  
  112. {  
  113.     bus_unregister(&ldd_bus_type);  
  114. }  
  115.   
  116. module_init(ldd_bus_init);  
  117. module_exit(ldd_bus_exit);  




    insmod bus.ko 之后发现,/sys/bus 目录下多了一个 ldd目录,这个目录就是我们向内核注册的 总线 ldd ,该目录下有一个devices 和 drivers目录,因为现在并没有向该总线注册任何的驱动和设备,因此这两个文件夹是空的。    cat version 会调用show函数,显示我们在 Bus 中设置的属性。二、driver[cpp] view plain copy
  1. struct device_driver {  
  2.     const char      *name;  
  3.     struct bus_type     *bus;  
  4.   
  5.     struct module       *owner;  
  6.     const char      *mod_name;  /* used for built-in modules */  
  7.   
  8.     bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */  
  9.   
  10.     int (*probe) (struct device *dev);  
  11.     int (*remove) (struct device *dev);  
  12.     void (*shutdown) (struct device *dev);  
  13.     int (*suspend) (struct device *dev, pm_message_t state);  
  14.     int (*resume) (struct device *dev);  
  15.     const struct attribute_group **groups;  
  16.   
  17.     const struct dev_pm_ops *pm;  
  18.   
  19.     struct driver_private *p;  
  20. };  
[cpp] view plain copy
  1. int driver_register(struct device_driver *drv)  
  2. {  
  3.     ret = bus_add_driver(drv);  
  4.     ret = driver_add_groups(drv, drv->groups);  
  5. }  
[cpp] view plain copy
  1. int bus_add_driver(struct device_driver *drv)  
  2. {  
  3.     struct bus_type *bus;  
  4.     struct driver_private *priv;  
  5.     int error = 0;  
  6.   
  7.     bus = bus_get(drv->bus);  
  8.   
  9.     priv = kzalloc(sizeof(*priv), GFP_KERNEL);  
  10.   
  11.     klist_init(&priv->klist_devices, NULL, NULL);  
  12.     priv->driver = drv;  
  13.     drv->p = priv;  
  14.     // 在/sys/bus/xxx/drivers 目录下创建目录  
  15.     priv->kobj.kset = bus->p->drivers_kset;  
  16.     error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,  
  17.                      "%s", drv->name);  
  18.     // 匹配 dev  
  19.     if (drv->bus->p->drivers_autoprobe) {  
  20.         error = driver_attach(drv);  
  21.     }  
  22.     // 将driver 加入 Bus->p->kist_drivers链表  
  23.     klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  
  24.     // 如果设置了drv->mod_name 根据名字寻找模块  
  25.     module_add_driver(drv->owner, drv);  
  26.     // 在/sys/bus/xxx/drivers/创建属性文件  
  27.     error = driver_create_file(drv, &driver_attr_uevent);  
  28.   
  29.     error = driver_add_attrs(bus, drv);  
  30.   
  31.     if (!drv->suppress_bind_attrs) {  
  32.         error = add_bind_files(drv);  
  33.     }  
  34.   
  35.     kobject_uevent(&priv->kobj, KOBJ_ADD);  
  36.     return 0;  
  37. }  
详细说一下driver匹配device的过程    在向Bus注册一个driver时,会调用到 driver_attch(drv) 来寻找与之配对的 deivice    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);    根据名字我们应该能猜测出来,调用Bus的每一个 dev 与 driver 进行  __driver_attach    在 __driver_attach 中,首先会调用到 driver_match_device 函数(return drv->bus->match ? drv->bus->match(dev, drv) : 1;)进行匹配,    如果匹配成功,则调用 driver_probe_device(drv, dev),然后调用 really_probe(dev, drv)     really_probe 中干了四件大事    1、dev->driver = drv;         在dev 中记录driver ,配对成功了嘛,在男方族谱上记录一下女方的名字。。然而device_driver结构中并没有device成员,因此并没有在女方族谱上记录男方的名字。    2、driver_sysfs_add(dev)        在sysfs中该 dev.kobj 目录下创建与之匹配的driver的符号连接,名字为“driver”        在sysfs中该 driver.kobj 目录下创建与之匹配的device的符号连接,名字为 kobject_name(&dev->kobj)     3、调用 probe         if (dev->bus->probe) {
    ret = dev->bus->probe(dev);
        } else if (drv->probe) {
    ret = drv->probe(dev);
        } 
    4、driver_bound         klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);        如果 device 未绑定到一个 driver 链表,则将这个 device 放入 driver 链表中,看来一个device只能有一个driver,但是driver可以支持多个device总结一下 driver_register 的工作    1、初始化 drv->priv->klist_devices 链表,该链表保存该驱动所支持的devices
    2、drv 与 priv 相互建立联系
    3、设置 drv->priv->kobj.kset = bus->p->drivers_kset;
    4、创建并注册 drv->priv->kobj ,设置 drv->priv->kobj.ktype = driver_ktype ,drv->priv->kobj.name = drv->name , drv->priv->kobj.parent = bus->p->drivers_kset.kobj 因此,会创建 /sys/bus/$(bus->name)/drivers/$(drv->name) 目录
    5、调用 drv->bus->match(dev, drv) ,匹配dev ,匹配成功调用probe函数
    6、将driver 加入 Bus->p->kist_drivers链表
    7、创建属性文件
    8、kobject_uevent(&priv->kobj, KOBJ_ADD);
下面来看个例子:[cpp] view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include    
  7. #include   
  8. #include   
  9. #include "lddbus.h"  
  10.   
  11. struct ldd_driver ldd_drv = {  
  12.   
  13.     .version    = "version 1.0",      
  14.     .driver = {  
  15.         .name = "myldd",  
  16.     },  
  17. };  
  18.   
  19. static int ldd_drv_init(void){  
  20.   
  21.     register_ldd_driver(&ldd_drv);  
  22.     return 0;  
  23. }  
  24.   
  25. static void ldd_drv_exit(void){  
  26.       
  27.     unregister_ldd_driver(&ldd_drv);  
  28. }  
  29.   
  30. module_init(ldd_drv_init);  
  31. module_exit(ldd_drv_exit);  
  32. MODULE_LICENSE("GPL");  



    insmod drv.ko 之后,我们会发现 /sys/bus/ldd/drivers 目录下多了一个 myldd 目录,这就是我们向内核注册的ldd总线上的myldd驱动程序。同样 cat version 会显示设定好的属性。三、device[cpp] view plain copy
  1. struct device {  
  2.     struct device       *parent;  
  3.     struct device_private   *p;  
  4.     struct kobject kobj;  
  5.     const char      *init_name; /* initial name of the device */  
  6.     struct device_type  *type;  
  7.     struct semaphore    sem;    /* semaphore to synchronize calls to                      
  8.     struct bus_type *bus;       /* type of bus device is on */  
  9.     struct device_driver *driver;   /* which driver has allocated this device */  
  10.     void        *platform_data; /* Platform specific data, device core doesn't touch it */  
  11.     struct dev_pm_info  power;  
  12.   
  13.     /* arch specific additions */  
  14.     struct dev_archdata archdata;  
  15.     dev_t           devt;   /* dev_t, creates the sysfs "dev" */  
  16.     spinlock_t      devres_lock;  
  17.     struct list_head    devres_head;  
  18.     struct klist_node   knode_class;  
  19.     struct class        *class;  
  20.     const struct attribute_group **groups;  /* optional groups */  
  21.     void    (*release)(struct device *dev);  
  22. };  
[cpp] view plain copy
  1. int device_register(struct device *dev)  
  2. {  
  3.     device_initialize(dev);  
  4.     return device_add(dev);  
  5. }  
[cpp] view plain copy
  1. void device_initialize(struct device *dev)  
  2. {  
  3.     // 设置 dev->kobj.kset 为 devices_kset  
  4.     dev->kobj.kset = devices_kset;  
  5.       
  6.     kobject_init(&dev->kobj, &device_ktype);  
  7.     INIT_LIST_HEAD(&dev->dma_pools);  
  8.     init_MUTEX(&dev->sem);  
  9.     spin_lock_init(&dev->devres_lock);  
  10.     INIT_LIST_HEAD(&dev->devres_head);  
  11.     device_init_wakeup(dev, 0);  
  12.     device_pm_init(dev);  
  13.     set_dev_node(dev, -1);  
  14. }  
[cpp] view plain copy
  1. int device_add(struct device *dev)  
  2. {  
  3.     struct device *parent = NULL;  
  4.     struct class_interface *class_intf;  
  5.   
  6.     dev = get_device(dev);  
  7.   
  8.     if (!dev->p) {  
  9.         error = device_private_init(dev);  
  10.     }  
  11.     // 如果设置了 init_name 将 init_name 设置为dev->kobj->name  
  12.     if (dev->init_name) {  
  13.         dev_set_name(dev, "%s", dev->init_name);  
  14.         dev->init_name = NULL;  
  15.     }  
  16.     // 将 parent_device.kobj 设置为dev->kobj->parent   
  17.     parent = get_device(dev->parent);  
  18.     setup_parent(dev, parent);  // 这里需要根据 实例分析  
  19.   
  20.     /* use parent numa_node */  
  21.     if (parent)  
  22.         set_dev_node(dev, dev_to_node(parent));  
  23.   
  24.     // 在 xxxx 目录下创建目录   xxxx是其父设备 例如platform_bus  
  25.     // 如果没有device->parent 则在/sys/ 目录下创建  
  26.     error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);  
  27.   
  28.     /* notify platform of device entry */  
  29.     if (platform_notify)  
  30.         platform_notify(dev);  
  31.     // 创建属性文件  
  32.     error = device_create_file(dev, &uevent_attr);  
  33.   
  34.     // 如果有主次设备号 创建dev 属性文件  
  35.     if (MAJOR(dev->devt)) {  
  36.         error = device_create_file(dev, &devt_attr);  
  37.   
  38.         error = device_create_sys_dev_entry(dev);  
  39.   
  40.         devtmpfs_create_node(dev);  
  41.     }  
  42.     // 从 dev->class->p->class_subsys.kobj 目录下创建到 /sys/devices/xxxx/subsystem 的软连接  
  43.     error = device_add_class_symlinks(dev);  
  44.     // 设置属性文件  
  45.     error = device_add_attrs(dev);  
  46.   
  47.     error = bus_add_device(dev);  
  48.   
  49.     error = dpm_sysfs_add(dev);  
  50.   
  51.     device_pm_add(dev);  
  52.   
  53.     /* Notify clients of device addition.  This call must come 
  54.      * after dpm_sysf_add() and before kobject_uevent(). 
  55.      */  
  56.     if (dev->bus)  
  57.         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,  
  58.                          BUS_NOTIFY_ADD_DEVICE, dev);  
  59.   
  60.     kobject_uevent(&dev->kobj, KOBJ_ADD);  
  61.     bus_probe_device(dev); // device_attach(dev); 匹配drv  
  62.     if (parent)  
  63.         klist_add_tail(&dev->p->knode_parent,  
  64.                    &parent->p->klist_children);  
  65.   
  66.     if (dev->class) {  
  67.   
  68.     }  
  69.   
  70. }  
[cpp] view plain copy
  1. int bus_add_device(struct device *dev)  
  2. {  
  3.   
  4.     if (bus) {  
  5.         // 创建属性文件  
  6.         error = device_add_attrs(bus, dev);  
  7.         // 创建 /sys/bus/$(bus->name)/devices/$(dev->name) 到 /sys/devices/$(dev->name) 的软连接  
  8.         error = sysfs_create_link(&bus->p->devices_kset->kobj,  
  9.                         &dev->kobj, dev_name(dev));  
  10.         // 创建 /sys/devices/$(dev->name)/subsystem 到 /sys/bus/$(bus->name)/devices/$(dev->name) 的软连接  
  11.         error = sysfs_create_link(&dev->kobj,  
  12.                 &dev->bus->p->subsys.kobj, "subsystem");  
  13.         // 创建 /sys/devices/$(dev->name)/bus 到 /sys/bus/$(bus->name)/devices/$(dev->name) 的软连接  
  14.         error = make_deprecated_bus_links(dev); // return sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "bus");  
  15.         // 将 dev 加入 bus->p->klist_devices 链表  
  16.         klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);  
  17.     }  
  18.     return 0;  
  19. }  
总结一下 device_register 函数的工作

    1、设置 dev->kobj.kset 为 devices_kset
    2、设置 dev->kobj.ktype 为 device_ktype
    3、如果设置了 init_name 将 init_name 设置为dev->kobj->name
    4、将 dev->kobj->parent 设置为 parent_device.kobj 
    5、在 xxxx 目录下创建目录   xxxx是其父设备 例如platform_bus 如果没有device->parent 则在/sys/ 目录下创建
    6、platform_notify
    7、创建属性文件
    8、如果设置了 设备号,则创建属性文件 dev 
    9、创建各种软连接,其中/sys/bus/xxx/devices/目录下 的目录 为 /sys/devices 目录的软件接
    10、blocking_notifier_call_chain
    11、kobject_uevent
    
12、bus_probe_device 匹配drv ,这个匹配过程和前面注册driver时是一样的,最终都会调用到 really_probe ,匹配成功则调用probe函数,不再赘述。

下面来看个例子:[cpp] view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include    
  7. #include   
  8. #include "lddbus.h"  
  9.   
  10. static dev_t devid;  
  11.   
  12. static struct ldd_device ldd_dev = {  
  13.     .name = "myldd",      
  14.     .dev = {          
  15.         .init_name = "myldd",  
  16.     },  
  17. };  
  18.   
  19. static int ldd_dev_init(void){  
  20.       
  21.     alloc_chrdev_region(&devid, 0, 1, "mylddtest");  
  22.     //ldd_dev.dev.devt = devid;  
  23.     register_ldd_device(&ldd_dev);  
  24.     return 0;  
  25. }  
  26.   
  27. static void ldd_dev_exit(void){  
  28.     unregister_ldd_device(&ldd_dev);  
  29. }  
  30.   
  31. module_init(ldd_dev_init);  
  32. module_exit(ldd_dev_exit);  
  33. MODULE_LICENSE("GPL");  



    device 相对driver 要复杂一些,insmod dev.ko 之后,我们可以在/sys/devices 目录下看到新增了一个目录 ldd0(ldd_bus) ,在 ldd0(ldd_bus)目录下