前面我们分析了设备驱动模型中的device和driver,device和driver本来是不相关的东西,只因为bus的存在,才被联系到了一起。本节就来看看设备驱动模型中起枢纽作用的bus。本节的头文件在include/linux/device.h和drivers/base/base.h,实现代码主要在bus.c中。因为在bus中有很多代码时为了device找到driver或者driver找到device而定义的,本节先尽量忽略这部分,专注于bus的注册和注销,属性定义等内容。剩下的留到讨论device和driver关系时在分析。

先来看看bus的数据结构。

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

struct bus_type是bus的通用数据结构。

name是bus的名称,注意到这里也是const char类型的,在sysfs中使用的还是kobj中动态创建的名称,这里的name只是初始名。

bus_attrs是bus为自己定义的一系列属性,dev_attrs是bus为旗下的device定义的一系列属性,drv_attrs是bus为旗下的driver定义的一系列属性。其中dev_attrs在bus_add_device()->device_add_attrs()中被加入dev目录下,drv_attrs在bus_add_driver()->driver_add_attrs()中被加入driver目录下。

match函数匹配总线中的dev和driver,返回值为1代表匹配成功,为0则失败。

uevent函数用于总线对uevent的环境变量添加,但在总线下设备的dev_uevent处理函数也有对它的调用。

probe函数是总线在匹配成功时调用的函数,bus->probe和drv->probe中只会有一个起效,同时存在时使用bus->probe。

remove函数在总线上设备或者驱动要删除时调用,bus->remove和drv->remove中同样只会有一个起效。

shutdown函数在所有设备都关闭时调用,即在core.c中的device_shutdown()函数中调用,bus->shutdown和drv->shutdown同样只会有一个起效。

suspend函数是在总线上设备休眠时调用。

resume函数是在总线上设备恢复时调用。

pm是struct dev_pm_ops类型,其中定义了一系列电源管理的函数。

p是指向bus_type_private的指针,其中定义了将bus同其它组件联系起来的变量。

  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. };
  11. #define to_bus(obj) container_of(obj, struct bus_type_private, subsys.kobj)

struct bus_type_private是将bus同device、driver、sysfs联系起来的结构。

subsys是kset类型,代表bus在sysfs中的类型。

drivers_kset代表bus目录下的drivers子目录。
devices_kset代表bus目录下地devices子目录。

klist_devices是bus的设备链表,klist_drivers是bus的驱动链表。

bus_notifier用于在总线上内容发送变化时调用特定的函数,这里略过。

driver_autoprobe标志定义是否允许device和driver自动匹配,如果允许会在device或者driver注册时就进行匹配工作。

bus指针指向struct bus_type类型。

使用struct bus_type_private可以将struct bus_type中的部分细节屏蔽掉,利于外界使用bus_type。struct  driver_private和struct device_private都有类似的功能。

  1. struct bus_attribute {
  2. struct attribute    attr;
  3. ssize_t (*show)(struct bus_type *bus, char *buf);
  4. ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
  5. };
  6. #define BUS_ATTR(_name, _mode, _show, _store)   \
  7. struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
  8. #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)

struct bus_attribute是bus对struct attribute类型的封装,更方便总线属性的定义。

  1. static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
  2. char *buf)
  3. {
  4. struct bus_attribute *bus_attr = to_bus_attr(attr);
  5. struct bus_type_private *bus_priv = to_bus(kobj);
  6. ssize_t ret = 0;
  7. if (bus_attr->show)
  8. ret = bus_attr->show(bus_priv->bus, buf);
  9. return ret;
  10. }
  11. static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
  12. const char *buf, size_t count)
  13. {
  14. struct bus_attribute *bus_attr = to_bus_attr(attr);
  15. struct bus_type_private *bus_priv = to_bus(kobj);
  16. ssize_t ret = 0;
  17. if (bus_attr->store)
  18. ret = bus_attr->store(bus_priv->bus, buf, count);
  19. return ret;
  20. }
  21. static struct sysfs_ops bus_sysfs_ops = {
  22. .show   = bus_attr_show,
  23. .store  = bus_attr_store,
  24. };
  25. static struct kobj_type bus_ktype = {
  26. .sysfs_ops  = &bus_sysfs_ops,
  27. };

以上应该是我们最熟悉的部分,bus_ktype中定义了bus对应的kset应该使用的kobj_type实例。与此类似,driver使用的是自定义的driver_ktype,device使用的是自定义的device_ktype。只是这里仅仅定义了sysfs_ops,并未定义release函数,不知bus_type_private打算何时释放。

  1. int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
  2. {
  3. int error;
  4. if (bus_get(bus)) {
  5. error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
  6. bus_put(bus);
  7. else
  8. error = -EINVAL;
  9. return error;
  10. }
  11. void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
  12. {
  13. if (bus_get(bus)) {
  14. sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
  15. bus_put(bus);
  16. }
  17. }

bus_create_file()在bus目录下创建属性文件,bus_remove_file()在bus目录下删除属性文件。类似的函数在driver和device中都有见到。

  1. static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
  2. {
  3. struct kobj_type *ktype = get_ktype(kobj);
  4. if (ktype == &bus_ktype)
  5. return 1;
  6. return 0;
  7. }
  8. static struct kset_uevent_ops bus_uevent_ops = {
  9. .filter = bus_uevent_filter,
  10. };
  11. static struct kset *bus_kset;

可以看到这里定义了一个bus_uevent_ops变量,这是kset对uevent事件处理所用的结构,它会用在bus_kset中。

  1. int __init buses_init(void)
  2. {
  3. bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
  4. if (!bus_kset)
  5. return -ENOMEM;
  6. return 0;
  7. }

在buses_init()中创建了/sys/bus目录,这是一个kset类型,使用了bus_uevent_ops的uevent操作类型。

其实这里的操作不难想象,在devices中我们有一个类似的devices_kset,可以回顾一下。

  1. static struct kset_uevent_ops device_uevent_ops = {
  2. .filter =   dev_uevent_filter,
  3. .name =     dev_uevent_name,
  4. .uevent =   dev_uevent,
  5. };
  6. /* kset to create /sys/devices/  */
  7. struct kset *devices_kset;
  8. int __init devices_init(void)
  9. {
  10. devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
  11. ...
  12. }
  13. void device_initialize(struct device *dev)
  14. {
  15. dev->kobj.kset = devices_kset;
  16. ...
  17. }

devices_kset在devices_init()中被创建,使用相应的device_uevent_ops进行uevent处理。而devices_kset又被设为每个device初始化时使用的kset。这就不难想象每个device都是以devices_kset为所属kset的,并使用device_uevent_ops中的处理函数。

只是这里还不知bus_kset会在哪里用到,或许是每个bus所属的kset吧,下面会有答案。

  1. static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
  2. {
  3. return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
  4. }
  5. static ssize_t store_drivers_autoprobe(struct bus_type *bus,
  6. const char *buf, size_t count)
  7. {
  8. if (buf[0] == '0')
  9. bus->p->drivers_autoprobe = 0;
  10. else
  11. bus->p->drivers_autoprobe = 1;
  12. return count;
  13. }
  14. static ssize_t store_drivers_probe(struct bus_type *bus,
  15. const char *buf, size_t count)
  16. {
  17. struct device *dev;
  18. dev = bus_find_device_by_name(bus, NULL, buf);
  19. if (!dev)
  20. return -ENODEV;
  21. if (bus_rescan_devices_helper(dev, NULL) != 0)
  22. return -EINVAL;
  23. return count;
  24. }
  25. static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
  26. static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
  27. show_drivers_autoprobe, store_drivers_autoprobe);

这里定义了总线下的两个属性,只写得drivers_probe,和可读写的drivers_autoprobe。至于其怎么实现的,我们现在还不关心。

  1. static int add_probe_files(struct bus_type *bus)
  2. {
  3. int retval;
  4. retval = bus_create_file(bus, &bus_attr_drivers_probe);
  5. if (retval)
  6. goto out;
  7. retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
  8. if (retval)
  9. bus_remove_file(bus, &bus_attr_drivers_probe);
  10. out:
  11. return retval;
  12. }
  13. static void remove_probe_files(struct bus_type *bus)
  14. {
  15. bus_remove_file(bus, &bus_attr_drivers_autoprobe);
  16. bus_remove_file(bus, &bus_attr_drivers_probe);
  17. }

add_probe_files()在bus目录下添加drivers_probe和drivers_autoprobe文件。

remove_probe_files()在bus目录下删除drivers_probe和drivers_autoprobe文件。

这两个函数对bus的probe类型属性进行管理,就像add_bind_files/remove_bind_files对driver的bind类型属性进行管理一样。

  1. static ssize_t bus_uevent_store(struct bus_type *bus,
  2. const char *buf, size_t count)
  3. {
  4. enum kobject_action action;
  5. if (kobject_action_type(buf, count, &action) == 0)
  6. kobject_uevent(&bus->p->subsys.kobj, action);
  7. return count;
  8. }
  9. static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);

上面定义了bus的一个属性uevent,用于bus所在的kset节点主动发起uevent消息。

同样地uevent文件在driver目录中也有见到。device目录中也有,不过除了store_uevent之外,还增加了show_uevent的功能。

  1. static struct device *next_device(struct klist_iter *i)
  2. {
  3. struct klist_node *n = klist_next(i);
  4. struct device *dev = NULL;
  5. struct device_private *dev_prv;
  6. if (n) {
  7. dev_prv = to_device_private_bus(n);
  8. dev = dev_prv->device;
  9. }
  10. return dev;
  11. }
  12. int bus_for_each_dev(struct bus_type *bus, struct device *start,
  13. void *data, int (*fn)(struct device *, void *))
  14. {
  15. struct klist_iter i;
  16. struct device *dev;
  17. int error = 0;
  18. if (!bus)
  19. return -EINVAL;
  20. klist_iter_init_node(&bus->p->klist_devices, &i,
  21. (start ? &start->p->knode_bus : NULL));
  22. while ((dev = next_device(&i)) && !error)
  23. error = fn(dev, data);
  24. klist_iter_exit(&i);
  25. return error;
  26. }
  27. struct device *bus_find_device(struct bus_type *bus,
  28. struct device *start, void *data,
  29. int (*match)(struct device *dev, void *data))
  30. {
  31. struct klist_iter i;
  32. struct device *dev;
  33. if (!bus)
  34. return NULL;
  35. klist_iter_init_node(&bus->p->klist_devices, &i,
  36. (start ? &start->p->knode_bus : NULL));
  37. while ((dev = next_device(&i)))
  38. if (match(dev, data) && get_device(dev))
  39. break;
  40. klist_iter_exit(&i);
  41. return dev;
  42. }

bus_for_each_dev()是以bus的设备链表中每个设备为参数,调用指定的处理函数。

bus_find_device()是寻找bus设备链表中的某个设备,使用指定的匹配函数。

这两个函数提供遍历bus的设备链表的方法,类似于drivers_for_each_device/drivers_find_device对driver的设备链表的遍历,device_for_each_child/device_find_child对device的子设备链表的遍历。

  1. static int match_name(struct device *dev, void *data)
  2. {
  3. const char *name = data;
  4. return sysfs_streq(name, dev_name(dev));
  5. }
  6. struct device *bus_find_device_by_name(struct bus_type *bus,
  7. struct device *start, const char *name)
  8. {
  9. return bus_find_device(bus, start, (void *)name, match_name);
  10. }

bus_find_device_by_name()给出了如何使用遍历函数的例子,寻找bus设备链表中指定名称的设备。

  1. static struct device_driver *next_driver(struct klist_iter *i)
  2. {
  3. struct klist_node *n = klist_next(i);
  4. struct driver_private *drv_priv;
  5. if (n) {
  6. drv_priv = container_of(n, struct driver_private, knode_bus);
  7. return drv_priv->driver;
  8. }
  9. return NULL;
  10. }
  11. int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
  12. void *data, int (*fn)(struct device_driver *, void *))
  13. {
  14. struct klist_iter i;
  15. struct device_driver *drv;
  16. int error = 0;
  17. if (!bus)
  18. return -EINVAL;
  19. klist_iter_init_node(&bus->p->klist_drivers, &i,
  20. start ? &start->p->knode_bus : NULL);
  21. while ((drv = next_driver(&i)) && !error)
  22. error = fn(drv, data);
  23. klist_iter_exit(&i);
  24. return error;
  25. }

bus_for_each_drv()对bus的驱动链表中的每个驱动调用指定的函数。

这和前面的bus_for_each_dev/bus_find_dev什么都是类似的,只是你可能怀疑为什么会没有bus_find_drv。是没有它的用武之地吗?

请看driver.c中的driver_find()函数。

  1. struct device_driver *driver_find(const char *name, struct bus_type *bus)
  2. {
  3. struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
  4. struct driver_private *priv;
  5. if (k) {
  6. priv = to_driver(k);
  7. return priv->driver;
  8. }
  9. return NULL;
  10. }

driver_find()函数是在bus的驱动链表中寻找指定名称的驱动,它的存在证明bus_find_drv()完全是用得上的。可linux却偏偏没有实现bus_find_drv。driver_find()的实现也因此一直走内层路线,它直接用kset_find_obj()进行kobect的名称匹配,调用to_driver()等内容将kobj转化为drv。首先这完全不同于bus_for_each_drv()等一系列遍历函数,它们走的都是在klist中寻找的路线,这里确实走的sysfs中kset内部链表。其次,这里其实也是获得了drv的一个引用计数,在kset_find_obj()中会增加匹配的kobj的引用计数,driver_find()并没有释放,就相当于获取了drv的一个引用计数。这样虽然也可以,但代码写得很不优雅。可见人无完人,linux代码还有许多可改进之处。当然,也可能在最新的linux版本中已经改正了。

  1. static int bus_add_attrs(struct bus_type *bus)
  2. {
  3. int error = 0;
  4. int i;
  5. if (bus->bus_attrs) {
  6. for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
  7. error = bus_create_file(bus, &bus->bus_attrs[i]);
  8. if (error)
  9. goto err;
  10. }
  11. }
  12. done:
  13. return error;
  14. err:
  15. while (--i >= 0)
  16. bus_remove_file(bus, &bus->bus_attrs[i]);
  17. goto done;
  18. }
  19. static void bus_remove_attrs(struct bus_type *bus)
  20. {
  21. int i;
  22. if (bus->bus_attrs) {
  23. for (i = 0; attr_name(bus->bus_attrs[i]); i++)
  24. bus_remove_file(bus, &bus->bus_attrs[i]);
  25. }
  26. }

bus_add_attrs()将bus->bus_attrs中定义的属性加入bus目录。

bus_remove_attrs()将bus->bus_attrs中定义的属性删除。

开始看struct bus_type时我们说到结构中的bus_attrs、dev_attrs、drv_attrs三种属性,后两者分别在device_add_attrs()和driver_add_attrs()中添加,最后的bus_attrs也终于在bus_add_attrs()中得到添加。只是它们虽然都定义在bus_type中,确实添加在完全不同的三个地方。

  1. static void klist_devices_get(struct klist_node *n)
  2. {
  3. struct device_private *dev_prv = to_device_private_bus(n);
  4. struct device *dev = dev_prv->device;
  5. get_device(dev);
  6. }
  7. static void klist_devices_put(struct klist_node *n)
  8. {
  9. struct device_private *dev_prv = to_device_private_bus(n);
  10. struct device *dev = dev_prv->device;
  11. put_device(dev);
  12. }

klist_devices_get()用于bus设备链表上添加节点时增加对相应设备的引用。

klist_devices_put()用于bus设备链表上删除节点时减少对相应设备的引用。

相似的函数是device中的klist_children_get/klist_children_put,这是device的子设备链表。除此之外,bus的驱动链表和driver的设备链表,都没有这种引用计数的保护。原因还未知,也许是linux觉得驱动不太靠谱,万一突然当掉,也不至于影响device的正常管理。

  1. /**
  2. * bus_register - register a bus with the system.
  3. * @bus: bus.
  4. *
  5. * Once we have that, we registered the bus with the kobject
  6. * infrastructure, then register the children subsystems it has:
  7. * the devices and drivers that belong to the bus.
  8. */
  9. int bus_register(struct bus_type *bus)
  10. {
  11. int retval;
  12. struct bus_type_private *priv;
  13. priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
  14. if (!priv)
  15. return -ENOMEM;
  16. priv->bus = bus;
  17. bus->p = priv;
  18. BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
  19. retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
  20. if (retval)
  21. goto out;
  22. priv->subsys.kobj.kset = bus_kset;
  23. priv->subsys.kobj.ktype = &bus_ktype;
  24. priv->drivers_autoprobe = 1;
  25. retval = kset_register(&priv->subsys);
  26. if (retval)
  27. goto out;
  28. retval = bus_create_file(bus, &bus_attr_uevent);
  29. if (retval)
  30. goto bus_uevent_fail;
  31. priv->devices_kset = kset_create_and_add("devices", NULL,
  32. &priv->subsys.kobj);
  33. if (!priv->devices_kset) {
  34. retval = -ENOMEM;
  35. goto bus_devices_fail;
  36. }
  37. priv->drivers_kset = kset_create_and_add("drivers", NULL,
  38. &priv->subsys.kobj);
  39. if (!priv->drivers_kset) {
  40. retval = -ENOMEM;
  41. goto bus_drivers_fail;
  42. }
  43. klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
  44. klist_init(&priv->klist_drivers, NULL, NULL);
  45. retval = add_probe_files(bus);
  46. if (retval)
  47. goto bus_probe_files_fail;
  48. retval = bus_add_attrs(bus);
  49. if (retval)
  50. goto bus_attrs_fail;
  51. pr_debug("bus: '%s': registered\n", bus->name);
  52. return 0;
  53. bus_attrs_fail:
  54. remove_probe_files(bus);
  55. bus_probe_files_fail:
  56. kset_unregister(bus->p->drivers_kset);
  57. bus_drivers_fail:
  58. kset_unregister(bus->p->devices_kset);
  59. bus_devices_fail:
  60. bus_remove_file(bus, &bus_attr_uevent);
  61. bus_uevent_fail:
  62. kset_unregister(&bus->p->subsys);
  63. kfree(bus->p);
  64. out:
  65. bus->p = NULL;
  66. return retval;
  67. }

bus_register()将bus注册到系统中。

先分配并初始化bus->p,名称使用bus->name,所属的kset使用bus_kset(果然不出所料),类型使用bus_ktype。bus_ktype的使用同driver中的driver_ktype,和device中的device_ktype一样,都是自定义的kobj_type,要知道kobj_type的使用关系到release函数,和自定义属性类型能否正常发挥。

调用kset_register()将bus加入sysfs,因为只是设置了kset,所以会被加入/sys/bus目录下。与driver直接加入相关总线的drivers目录类似,却是与device复杂的寻找父节点过程相去甚远。

在bus目录下添加uevent属性。

在bus目录下创建devices子目录。它是一个kset类型的,目的是展示bus下的设备链表。

在bus目录下创建drivers子目录。它也是一个kset类型的,目的是展示bus下的驱动链表。

或许在最开始有设备驱动模型时,还需要kset来表达这种链表关系,但随着klist等结构的加入,kset的作用也越来越少,现在更多的作用是用来处理uevent消息。

之后初始化bus的设备链表和驱动链表,其中设备链表会占用设备的引用计数。

调用add_probe_files()在bus目录下添加probe相关的两个属性文件。

调用bus_add_attrs添加bus结构中添加的属性。

bus_register()中的操作出乎意料的简单。bus既不需要在哪里添加软链接,也不需要主动向谁报道,从来都是device和driver到bus这里报道的。所以bus_register()中只需要初始一下结构,添加到sysfs中,添加相关的子目录和属性文件,就行了。

  1. void bus_unregister(struct bus_type *bus)
  2. {
  3. pr_debug("bus: '%s': unregistering\n", bus->name);
  4. bus_remove_attrs(bus);
  5. remove_probe_files(bus);
  6. kset_unregister(bus->p->drivers_kset);
  7. kset_unregister(bus->p->devices_kset);
  8. bus_remove_file(bus, &bus_attr_uevent);
  9. kset_unregister(&bus->p->subsys);
  10. kfree(bus->p);
  11. bus->p = NULL;
  12. }

bus_unregister()与bus_register()相对,将bus从系统中注销。不过要把bus注销也不是那么简单的,bus中的driver和device都对bus保有一份引用计数。或许正是如此,bus把释放bus->p的动作放在了bus_unregister()中,这至少能保证较早地释放不需要的内存空间。而且在bus引用计数用完时,也不会有任何操作,bus的容错性还是很高的。

  1. static struct bus_type *bus_get(struct bus_type *bus)
  2. {
  3. if (bus) {
  4. kset_get(&bus->p->subsys);
  5. return bus;
  6. }
  7. return NULL;
  8. }
  9. static void bus_put(struct bus_type *bus)
  10. {
  11. if (bus)
  12. kset_put(&bus->p->subsys);
  13. }

bus_get()增加对bus的引用计数,bus_put()减少对bus的引用计数。实际上这里bus的引用计数降为零时,只是将sysfs中bus对应的目录删除。

无论是bus,还是device,还是driver,都是将主要的注销工作放在相关的unregister中。至于在引用计数降为零时的操作,大概只在device_release()中可见。这主要是因为引用计数,虽然是广泛用在设备驱动模型中,但实际支持的,绝大部分是设备的热插拔,而不是总线或者驱动的热插拔。当然,桥设备的热插拔也可能附带总线的热插拔。

  1. /*
  2. * Yes, this forcably breaks the klist abstraction temporarily.  It
  3. * just wants to sort the klist, not change reference counts and
  4. * take/drop locks rapidly in the process.  It does all this while
  5. * holding the lock for the list, so objects can't otherwise be
  6. * added/removed while we're swizzling.
  7. */
  8. static void device_insertion_sort_klist(struct device *a, struct list_head *list,
  9. int (*compare)(const struct device *a,
  10. const struct device *b))
  11. {
  12. struct list_head *pos;
  13. struct klist_node *n;
  14. struct device_private *dev_prv;
  15. struct device *b;
  16. list_for_each(pos, list) {
  17. n = container_of(pos, struct klist_node, n_node);
  18. dev_prv = to_device_private_bus(n);
  19. b = dev_prv->device;
  20. if (compare(a, b) <= 0) {
  21. list_move_tail(&a->p->knode_bus.n_node,
  22. &b->p->knode_bus.n_node);
  23. return;
  24. }
  25. }
  26. list_move_tail(&a->p->knode_bus.n_node, list);
  27. }
  28. void bus_sort_breadthfirst(struct bus_type *bus,
  29. int (*compare)(const struct device *a,
  30. const struct device *b))
  31. {
  32. LIST_HEAD(sorted_devices);
  33. struct list_head *pos, *tmp;
  34. struct klist_node *n;
  35. struct device_private *dev_prv;
  36. struct device *dev;
  37. struct klist *device_klist;
  38. device_klist = bus_get_device_klist(bus);
  39. spin_lock(&device_klist->k_lock);
  40. list_for_each_safe(pos, tmp, &device_klist->k_list) {
  41. n = container_of(pos, struct klist_node, n_node);
  42. dev_prv = to_device_private_bus(n);
  43. dev = dev_prv->device;
  44. device_insertion_sort_klist(dev, &sorted_devices, compare);
  45. }
  46. list_splice(&sorted_devices, &device_klist->k_list);
  47. spin_unlock(&device_klist->k_lock);
  48. }

bus_sort_breadthfirst()是将bus的设备链表进行排序,使用指定的比较函数,排成降序。

本节主要分析了bus的注册注销过程,下节我们将深入分析device和driver的绑定过程,了解bus在这其中到底起了什么作用。随着我们了解的逐渐深入,未知的东西也在逐渐增多。但饭要一口一口吃,我们的分析也要一点一点来,急不得。

Linux内核部件分析 设备驱动模型之bus相关推荐

  1. Linux内核部件分析 设备驱动模型之driver ---mark 详细

    Linux内核部件分析 设备驱动模型之driver 转载:https://www.linuxidc.com/Linux/2011-10/44627p7.htm 上节我们分析设备驱动模型中的device ...

  2. [转载]Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    [转载]Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1) 2011年09月13日 可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备 ...

  3. linux内核部件分析之——设备驱动模型之class

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  4. linux内核部件分析(十)——设备驱动模型之class,linux内核部件分析(十)——设备驱动模型之class...

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  5. linux内核组件分析之--设备驱动模型之bus

    前面我们分析了设备驱动模型中的device和driver,device和driver本来是不相关的东西,只因为bus的存在,才被联系到了一起.本节就来看看设备驱动模型中起枢纽作用的bus.本节的头文件 ...

  6. linux内核的块设备驱动框架详解

    1.块设备和字符设备的差异 (1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位.大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作; (2)块设备对于 I/O 请求 ...

  7. Linux内核大讲堂之设备驱动的基石驱动模型(1)

    转自 http://blog.csdn.net/gqb_driver/article/details/8449588 转自:无为和尚的Linux内核大讲堂系列,并对个别地方进行了补充更正(见标红处). ...

  8. Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备驱动的基石.大部分人把驱动模型叫做设备模型,但是我查了linux的帮助文档,就是在下载源码路 ...

  9. 【转】Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    原文传送门http://blog.csdn.net/z2007b/archive/2011/05/03/6388753.aspx 可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会 ...

  10. linux设备驱动模型 - device/bus/driver

    在linux驱动模型中,为了便于管理各种设备,我们把不同设备分别挂在他们对应的总线上,设备对应的驱动程序也在总线上找,这样就提出了deivce-bus-driver的模型,硬件上有许多设备总线,那么我 ...

最新文章

  1. jquery autocomplete实现solr查询字段自动填充并执行查询
  2. Linux学习之CentOS(二十三)--Linux软件管理之源代码以及RPM软件包管理
  3. Matlab标识指令中字符的精细控制
  4. 计算机组成原理1--原码、反码、补码、移码之间的关系
  5. linux bash文件,linux之bash配置文件
  6. 5款最适合新手的包管理器
  7. 《无线网络:理解和应对互联网环境下网络互连所带来的挑战》——第2章 无线生态系统 2.1无线标准化过程...
  8. ecplice中class.forname一直报错_A6v5.1升级A6v7.0报错:调用Java代码
  9. Save the Room【找规律】
  10. android oom工具,Android OOM-Heap,MAT工具检测内存泄露
  11. Git 分支管理 Feature分支 强行删除分支
  12. SingToken全球首款区块链智能AI音乐钱包
  13. 环形线圈车辆检测器突破了LOOP-LOCK
  14. 一起学Windows phone7开发(五.一个时钟的例子)
  15. fpga初始化错误_FPGA下载程序出错
  16. 登陆失败:用户账户限制。可能的原因包括不允许空密码.........解决方案
  17. 实现自动WiFi连接
  18. MAC终端连接服务器问题杂记
  19. Mysql高级-应用优化,查询缓存优化,锁
  20. 护眼党必备良心app

热门文章

  1. 推荐系统在房产领域的实践
  2. 【内购篇】5 分钟教你成为会赚钱的独立开发者
  3. python 异常学习2
  4. 二分图最大权匹配:Kuhn-Munkres算法
  5. [C#]System.Timers.Timer(2)
  6. 2013年7月3日星期三
  7. 第一堂TCP/IP课
  8. myeclipse 配置 SVN
  9. HDFS-异常大全-《每日五分钟搞定大数据》
  10. Android中如何使用Intent在Activity之间传递对象[使用Serializable或者Parcelable]