尽管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

[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. 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. };
[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. 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 内核

[cpp] view
plain
 copy

  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 *);
[cpp] view
plain
 copy

  1. #include <linux/device.h>
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/init.h>
  5. #include <linux/string.h>
  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\n", 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\n");
  30. }
  31. static void ldd_dev_release(struct device *dev){ }
  32. struct device ldd_bus = {
  33. .init_name   = "ldd0",<span style="white-space:pre">  </span>// 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\n", 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\n");
  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

[cpp] view
plain
 copy

  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. };
[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. 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);

下面来看个例子:

[cpp] view
plain
 copy

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/init.h>
  5. #include <linux/device.h>
  6. #include <linux/sched.h>
  7. #include <asm/uaccess.h>
  8. #include <linux/io.h>
  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

[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. /* arch specific additions */
  13. struct dev_archdata archdata;
  14. dev_t           devt;   /* dev_t, creates the sysfs "dev" */
  15. spinlock_t      devres_lock;
  16. struct list_head    devres_head;
  17. struct klist_node   knode_class;
  18. struct class        *class;
  19. const struct attribute_group **groups;  /* optional groups */
  20. void    (*release)(struct device *dev);
  21. };
[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. kobject_init(&dev->kobj, &device_ktype);
  6. INIT_LIST_HEAD(&dev->dma_pools);
  7. init_MUTEX(&dev->sem);
  8. spin_lock_init(&dev->devres_lock);
  9. INIT_LIST_HEAD(&dev->devres_head);
  10. device_init_wakeup(dev, 0);
  11. device_pm_init(dev);
  12. set_dev_node(dev, -1);
  13. }
[cpp] view
plain
 copy

  1. int device_add(struct device *dev)
  2. {
  3. struct device *parent = NULL;
  4. struct class_interface *class_intf;
  5. dev = get_device(dev);
  6. if (!dev->p) {
  7. error = device_private_init(dev);
  8. }
  9. // 如果设置了 init_name 将 init_name 设置为dev->kobj->name
  10. if (dev->init_name) {
  11. dev_set_name(dev, "%s", dev->init_name);
  12. dev->init_name = NULL;
  13. }
  14. // 将 parent_device.kobj 设置为dev->kobj->parent
  15. parent = get_device(dev->parent);
  16. setup_parent(dev, parent);  // 这里需要根据 实例分析
  17. /* use parent numa_node */
  18. if (parent)
  19. set_dev_node(dev, dev_to_node(parent));
  20. // 在 xxxx 目录下创建目录   xxxx是其父设备 例如platform_bus
  21. // 如果没有device->parent 则在/sys/ 目录下创建
  22. error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
  23. /* notify platform of device entry */
  24. if (platform_notify)
  25. platform_notify(dev);
  26. // 创建属性文件
  27. error = device_create_file(dev, &uevent_attr);
  28. // 如果有主次设备号 创建dev 属性文件
  29. if (MAJOR(dev->devt)) {
  30. error = device_create_file(dev, &devt_attr);
  31. error = device_create_sys_dev_entry(dev);
  32. devtmpfs_create_node(dev);
  33. }
  34. // 从 dev->class->p->class_subsys.kobj 目录下创建到 /sys/devices/xxxx/subsystem 的软连接
  35. error = device_add_class_symlinks(dev);
  36. // 设置属性文件
  37. error = device_add_attrs(dev);
  38. error = bus_add_device(dev);
  39. error = dpm_sysfs_add(dev);
  40. device_pm_add(dev);
  41. /* Notify clients of device addition.  This call must come
  42. * after dpm_sysf_add() and before kobject_uevent().
  43. */
  44. if (dev->bus)
  45. blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
  46. BUS_NOTIFY_ADD_DEVICE, dev);
  47. kobject_uevent(&dev->kobj, KOBJ_ADD);
  48. bus_probe_device(dev); // device_attach(dev); 匹配drv
  49. if (parent)
  50. klist_add_tail(&dev->p->knode_parent,
  51. &parent->p->klist_children);
  52. if (dev->class) {
  53. }
  54. }
[cpp] view
plain
 copy

  1. int bus_add_device(struct device *dev)
  2. {
  3. if (bus) {
  4. // 创建属性文件
  5. error = device_add_attrs(bus, dev);
  6. // 创建 /sys/bus/$(bus->name)/devices/$(dev->name) 到 /sys/devices/$(dev->name) 的软连接
  7. error = sysfs_create_link(&bus->p->devices_kset->kobj,
  8. &dev->kobj, dev_name(dev));
  9. // 创建 /sys/devices/$(dev->name)/subsystem 到 /sys/bus/$(bus->name)/devices/$(dev->name) 的软连接
  10. error = sysfs_create_link(&dev->kobj,
  11. &dev->bus->p->subsys.kobj, "subsystem");
  12. // 创建 /sys/devices/$(dev->name)/bus 到 /sys/bus/$(bus->name)/devices/$(dev->name) 的软连接
  13. error = make_deprecated_bus_links(dev); // return sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "bus");
  14. // 将 dev 加入 bus->p->klist_devices 链表
  15. klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
  16. }
  17. return 0;
  18. }

总结一下 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 <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/init.h>
  5. #include <linux/device.h>
  6. #include <linux/sched.h>
  7. #include <asm/uaccess.h>
  8. #include "lddbus.h"
  9. static dev_t devid;
  10. static struct ldd_device ldd_dev = {
  11. .name = "myldd",
  12. .dev = {
  13. .init_name = "myldd",
  14. },
  15. };
  16. static int ldd_dev_init(void){
  17. alloc_chrdev_region(&devid, 0, 1, "mylddtest");
  18. //ldd_dev.dev.devt = devid;
  19. register_ldd_device(&ldd_dev);
  20. return 0;
  21. }
  22. static void ldd_dev_exit(void){
  23. unregister_ldd_device(&ldd_dev);
  24. }
  25. module_init(ldd_dev_init);
  26. module_exit(ldd_dev_exit);
  27. MODULE_LICENSE("GPL");

device 相对driver 要复杂一些,insmod dev.ko 之后,我们可以在/sys/devices 目录下看到新增了一个目录 ldd0(ldd_bus) ,在 ldd0 (ldd_bus)目录下看到我们向ldd总线注册的myldd设备(ldd0是
myldd 的父设备),在/sys/bus/ldd/devices/ 目录下同样可以看到 myldd , 因为这里的Myldd 是指向 /sys/devices/ldd0/myldd 的软连接。

/sys/devices/ldd0/myldd/driver 目录 与该设备匹配的驱动程序,我们在Bus->match中设置的匹配条件--名字相同。

我们并未看到属性文件 dev ,是因为我们没有指定Myldd设备的设备号,将 dev.c 代码中的 ldd_dev.dev.devt = devid 注释去掉,卸载原来驱动,重新加载。

我们会发现,现在有了属性文件 dev ,cat dev 显示的是该设备的设备号,在此条件之下,mdev 会帮我们创建设备节点,因此在 /dev 目录下已经有了设备节点。

至此,简单的设备总线驱动模型就结束了,虽然例子很简单,但我相信对以后的理解各类负责的总线设备驱动模型也是会有帮助的。

转自: http://blog.csdn.net/lizuobin2/

Linux 设备总线驱动模型的更多相关文章

  1. Linux 总线、设备、驱动模型 与 设备树

    1.总线.设备.驱动模型 本着高内聚.低耦合的原则,Linux 把设备驱动模型分为了总线.设备和驱动三个实体,这三个实体在内核里的职责分别如下: 设备和驱动向总线进行注册,总线负责把设备和对应的驱动绑 ...

  2. 让天堂的归天堂,让尘土的归尘土——谈Linux的总线、设备、驱动模型

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 公元1951年5月15日的国会听证上, ...

  3. linux usb总线驱动(一)

    目录 linux usb总线驱动框架 USB 介绍 传输类型 控制器接口 2440接口 基本流程 alloc_dev choose_address hub_port_init usb_get_devi ...

  4. Linux平台总线驱动设备模型

    platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为pl ...

  5. linux平台总线驱动设备模型之点亮LED

    这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备. 问:如何编写平台总线驱动设备这一套架构的设备驱动? ...

  6. 驱动04.平台总线驱动模型——点亮LED灯

    1 平台总线的简介 平台总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的 ...

  7. linux设备和驱动加载的先后顺序

    点击打开链接 Linux驱动先注册总线,总线上可以先挂device,也可以先挂driver,那么究竟怎么控制先后的顺序呢. Linux系统使用两种方式去加载系统中的模块:动态和静态. 静态加载:将所有 ...

  8. 4412 Linux设备总线

    总线_设备_驱动注册流程详解 注册流程图 • 设备一般都需要先注册,才能注册驱动– 现在越来越多的热拔插设备,反过来了.先注册驱动,设备来了再注册 设备 • 本节使用的命令– 查看总线的命令#ls / ...

  9. Linux设备总线

    kobject和kset是Linux设备模型中最基本的元素,其中,kset是同种类型kobject对象的集合.每个在内核中注册的kobject对象都对于sysfs文件系统中的一个目录.下面是自己花的一 ...

随机推荐

  1. APUE学习笔记——4.2结构体 struct stat 及其相关函数介绍

    以下不少内容来自man手册 结构体struct stat         结构体struct stat用于保存文件相关的所有信息.         struct stat的基本成员如下所示 struc ...

  2. (转)使用Hive UDF和GeoIP库为Hive加入IP识别功能

    Hive是基于Hadoop的数据管理系统,作为分析人员的即时分析工具和ETL等工作的执行引擎,对于如今的大数据管理与分析.处理有着非常大的 意义.GeoIP是一套IP映射数据库,它定时更新,并且提供了 ...

  3. EasyDSS流媒体服务器实现RTMP直播同步HLS录像和时移回放

    本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/74166337 "目前在市面上可以找到的 ...

  4. Realm的常规使用与线程中的坑

    结识 Realm 的催化剂 在我们公司的项目迭代中,由于在之前的聊天这个模块关于用户信息的传值有问题,而之前因为项目经过很多开发者的手,且不提整体的架构有多混乱,就单说缓存这块,就是乱的不行,有的地方 ...

  5. Weinre(pc调试手机页面)

         Weinre是一款基于Web Inspector(Webkit)的远程调试工具,借助于网络,可以在PC上直接调试运行在移动设备上的远程页面,中文意思是远程Web检查器,有了Weinre,在P ...

  6. ibatis 参数类型为map,map里面有list

    <select id="getChannelLayerList" parameterClass="java.util.HashMap"        re ...

  7. python 之头像上传,预览

    上传头像 我们需要实现的效果是:当我们点击默认头像,用户可以进行选择要上传的头像文件,其原理就是头像的img标签与文件input(file类型)框重合或者关联,可以通过如下两种方式进行实现: 方法一l ...

  8. 【剑指offer15】二进制中1的个数(位运算),C++实现

    原创博文,转载请注明出处! # 本文是牛客网<剑指offer>刷题笔记 1.题目 # 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示.例如,把9表示成二进制是1001,有两 ...

  9. 【集成学习】lightgbm调参案例

    lightgbm使用leaf_wise tree生长策略,leaf_wise_tree的优点是收敛速度快,缺点是容易过拟合. # lightgbm关键参数 # lightgbm调参方法cv 代码git ...

  10. 文件的copy

    def mycopy(src_filename, dst_filename): try: fr = open(src_filename, "rb") try: try: fw = ...