Linux 设备总线驱动模型(转载)

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux驱动总线设备模型(Driver model)是一种抽象的设备模型,用于描述系统中各种设备设备之间的关系。它提供了一种标准的设备访问接口,使得设备驱动程序可以独立于硬件平台而存在,从而提高了设备驱动程序的可移植性和可维护性。 在Linux中,驱动总线设备模型包括以下几个主要的概念: 1. 总线(Bus):总线是一种连接设备的物理或逻辑结构,用于实现设备之间的通信。Linux支持多种总线类型,例如PCI、USB、I2C等。 2. 设备Device):设备是指在总线上注册的硬件设备,每个设备都有一个唯一的设备树路径和设备标识符。设备可以包括子设备和属性,例如硬件资源、中断、供电等信息。 3. 驱动程序(Driver):驱动程序是指用于控制设备的软件程序,它通过向设备发送命令和读取设备的状态来实现对设备的控制。驱动程序可以注册到总线上,当设备被插入到总线上时,总线会自动匹配相应的驱动程序并加载它。 4. 类(Class):类是一组具有相似功能的设备的集合,例如输入设备、网络设备、存储设备等。类提供了一些通用的接口和属性,使得驱动程序可以更加方便地操作设备Linux驱动总线设备模型的主要优点包括: 1. 支持多种总线类型,使得驱动程序可以在不同的硬件平台上运行。 2. 提供标准的设备访问接口,使得驱动程序可以独立于硬件平台而存在。 3. 支持设备热插拔和动态配置,使得系统更加灵活和可扩展。 4. 提供了类的概念,使得驱动程序可以更加方便地操作设备

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值