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

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

  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. };
  1. int device_register(struct device *dev)
  2. {
  3. device_initialize(dev);
  4. return device_add(dev);
  5. }
  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. }
  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. }
  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函数,不再赘述。

下面来看个例子:

  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 目录下已经有了设备节点。

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

原文链接:https://blog.csdn.net/lizuobin2/article/details/51570196

Linux 设备总线驱动模型(转载)相关推荐

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

    一.Linux总线设备驱动模型框架 从Linux2.6开始Linux加入了一套驱动管理和注册机制-platform平台总线驱动模型.platform平台总线是一条虚拟总线,platform_devic ...

  2. linux驱动:设备-总线-驱动(以TI+DM8127中GPIO为例)

    一:说明:这次学习设备-总线-驱动是以TI+DM8127的GPIO为例 1.GPIO资源注册到omap_hwmod链表中 2.初始化GPIO 3.将GPIO注册到plarform层 4.将GPIO注册 ...

  3. Linux设备与驱动学习之----什么是设备

    [ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在学习Linux设备驱动的过程中我们用到也是看到最多的就是设备和驱动了,接下 ...

  4. Linux 总线、设备、驱动模型的探究

    学习交流加 个人qq:1126137994 个人微信:liu1126137994 学习交流资源分享qq群:962535112 之前一直做项目,做项目的过程虽然也学习到了不少知识,但是,一直没有好好研究 ...

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

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

  6. 【嵌入式环境下linux内核及驱动学习笔记-(16)linux总线、设备、驱动模型之input框架】

    目录 1.Linux内核输入子系统概念导入 1.1 输入设备工作机制 1.2 运行框架 1.3 分层思想 2.驱动开发步骤 2.1 在init()或probe()函数中 2.2 在exit()或rem ...

  7. Linux设备驱动---OMAP3630 Linux I2C总线驱动分析(1)

    原文地址:http://blog.csdn.net/kellycan/article/details/6394737 1 Linux I2C驱动架构 Linux下I2C驱动的架构图如下: 图1.1 L ...

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

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

  9. 宋宝华:Linux设备与驱动的手动解绑与手动绑定

    众所周知,Linux靠设备与驱动之间的match,来完成设备与驱动的bind,从而触发驱动的probe()成员函数被执行.每个bus都有相应的match方法,完成match的总的入口函数是: stat ...

最新文章

  1. 多线程(十、AQS原理-ReentrantLock公平锁)
  2. SpringSide 4 QuickStart运行Demo
  3. 透视变换–鸟瞰图_单例设计模式–鸟瞰
  4. 打开服务器文件提示内存不够,打开服务器文件提示内存不够
  5. SwarmKit知多少——来自源码世界的深入解读
  6. 公司内部z区域网络升级可行性方案
  7. ortp编程示例代码
  8. 单元测试的必要性 从bug修复 费用成本和时间成本综合考虑
  9. 《C专家编程》:编译器的金科玉律(一)
  10. AHA加速器测试,安卓、IOS、windos、mac测试正常
  11. html中css鼠标手势样式,CSS鼠标手势
  12. U盘有必要安全弹出吗??
  13. 三分钟读懂新一代人工智能——ChatGPT
  14. Java 中各种DTO,POJO 等的概念
  15. 五子棋棋谱16*16c语言,五子棋开局棋谱大全
  16. 万里挑一!热门机器学习开源资源最强盘点
  17. 计算机格式化什么意思,u盘格式化是什么意思,教您u盘格式化是什么意思
  18. 虚拟主机还是云服务器玩游戏,虚拟主机还是云服务器玩游戏
  19. 京东股权众筹平台的玩法和价值
  20. [转]英文中的偏旁部首及其记忆

热门文章

  1. 学习PHP中的信息格式化操作
  2. iOS形变之CGAffineTransform
  3. 2021年9月16日
  4. 利用ajax验证用户名,3.6.2 利用Ajax验证注册用户名(1)
  5. python之JSON字符串处理
  6. 数学建模竞赛常用算法介绍及对应国赛获奖论文分类整理分享
  7. 双十一京东图书购物清单,动动脑子节省300元
  8. 弘辽科技:猜你喜欢要怎么样入池呢?
  9. 冒险岛的服务器维护时间,维护公告!9月21日零点服务器维护
  10. [附源码]计算机毕业设计JAVA食堂线上点餐系统