驱动专题:第一章驱动框架 1. Linux 设备驱动总线模型

2019-07-13 07:21发布

尽管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/lizuobin2/article/details/51523693
    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 
  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
  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. int (*match)(struct device *dev, struct device_driver *drv);
  7. int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
  8. int (*probe)(struct device *dev);
  9. int (*remove)(struct device *dev);
  10. void (*shutdown)(struct device *dev);
  11. int (*suspend)(struct device *dev, pm_message_t state);
  12. int (*resume)(struct device *dev);
  13. const struct dev_pm_ops *pm;
  14. struct bus_type_private *p;
  15. };
  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. };
  1. int bus_register(struct bus_type *bus)
  2. {
  3. int retval;
  4. struct bus_type_private *priv;
  5. priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
  6. /* 1. bus 与 prv 相互建立联系 */
  7. // 私有数据 .bus -> bus 本身
  8. priv->bus = bus;
  9. // bus->p 指向 priv
  10. bus->p = priv;
  11. // 内核通知链
  12. BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
  13. /* 设置 bus->prv->subsys->kobj */
  14. // 设置 priv->subsys.kobj.name = bus->name 对应于/sys/ 目录下的目录名
  15. retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
  16. // 所有的 priv->subsys.kobj.kset 指向 bus_kse 对应于图中④与六的关系
  17. priv->subsys.kobj.kset = bus_kset;
  18. // 所有的priv->subsys.kobj.ktype 等于 bus_ktype
  19. priv->subsys.kobj.ktype = &bus_ktype;
  20. priv->drivers_autoprobe = 1;
  21. /* 注册 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */
  22. // 注册 priv->subsys ,由于 priv->subsys.kobj.kset = bus_kset,所以会在 /sys/bus/目录下创建 目录 如/sys/bus/plateform
  23. retval = kset_register(&priv->subsys);
  24. // sysfs_create_file(&bus->p->subsys.kobj, &bus_attr_uevent->attr);
  25. retval = bus_create_file(bus, &bus_attr_uevent);
  26. // 由于 priv->subsys.kobj.kset = bus_kset ,因此会创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/devices
  27. priv->devices_kset = kset_create_and_add("devices", NULL,
  28. &priv->subsys.kobj);
  29. // 同理 创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/drivers
  30. priv->drivers_kset = kset_create_and_add("drivers", NULL,
  31. &priv->subsys.kobj);
  32. // 初始化 klist_devices 并设置get put 函数 初始化 klist_drivers 不知为何没有get put ?
  33. klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
  34. klist_init(&priv->klist_drivers, NULL, NULL);
  35. retval = add_probe_files(bus); // static inline int add_probe_files(struct bus_type *bus) { return 0; }
  36. // 添加 bus->attrs 属性文件
  37. retval = bus_add_attrs(bus);
  38. return 0;
  39. }

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




    insmod bus.ko 之后发现,/sys/bus 目录下多了一个 ldd目录,这个目录就是我们向内核注册的 总线 ldd ,该目录下有一个devices 和 drivers目录,因为现在并没有向该总线注册任何的驱动和设备,因此这两个文件夹是空的。    cat version 会调用show函数,显示我们在 Bus 中设置的属性。二、driver
  1. struct device_driver {
  2. const char *name;
  3. struct bus_type *bus;
  4. struct module *owner;
  5. const char *mod_name; /* used for built-in modules */
  6. bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
  7. int (*probe) (struct device *dev);
  8. int (*remove) (struct device *dev);
  9. void (*shutdown) (struct device *dev);
  10. int (*suspend) (struct device *dev, pm_message_t state);
  11. int (*resume) (struct device *dev);
  12. const struct attribute_group **groups;
  13. const struct dev_pm_ops *pm;
  14. struct driver_private *p;
  15. };
  1. int driver_register(struct device_driver *drv)
  2. {
  3. ret = bus_add_driver(drv);
  4. ret = driver_add_groups(drv, drv->groups);
  5. }
  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. bus = bus_get(drv->bus);
  7. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  8. klist_init(&priv->klist_devices, NULL, NULL);
  9. priv->driver = drv;
  10. drv->p = priv;
  11. // 在/sys/bus/xxx/drivers 目录下创建目录
  12. priv->kobj.kset = bus->p->drivers_kset;
  13. error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
  14. "%s", drv->name);
  15. // 匹配 dev
  16. if (drv->bus->p->drivers_autoprobe) {
  17. error = driver_attach(drv);
  18. }
  19. // 将driver 加入 Bus->p->kist_drivers链表
  20. klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
  21. // 如果设置了drv->mod_name 根据名字寻找模块
  22. module_add_driver(drv->owner, drv);
  23. // 在/sys/bus/xxx/drivers/创建属性文件
  24. error = driver_create_file(drv, &driver_attr_uevent);
  25. error = driver_add_attrs(bus, drv);
  26. if (!drv->suppress_bind_attrs) {
  27. error = add_bind_files(drv);
  28. }
  29. kobject_uevent(&priv->kobj, KOBJ_ADD);
  30. return 0;
  31. }
详细说一下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);
下面来看个例子:
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include "lddbus.h"
  10. struct ldd_driver ldd_drv = {
  11. .version = "version 1.0",
  12. .driver = {
  13. .name = "myldd",
  14. },
  15. };
  16. static int ldd_drv_init(void){
  17. register_ldd_driver(&ldd_drv);
  18. return 0;
  19. }
  20. static void ldd_drv_exit(void){
  21. unregister_ldd_driver(&ldd_drv);
  22. }
  23. module_init(ldd_drv_init);
  24. module_exit(ldd_drv_exit);
  25. MODULE_LICENSE("GPL");



    insmod drv.ko 之后,我们会发现 /sys/bus/ldd/drivers 目录下多了一个 myldd 目录,这就是我们向内核注册的ldd总线上的myldd驱动程序。同样 cat version 会显示设定好的属性。三、device
  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