前面我们分析了设备驱动模型中的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的数据结构。
- struct bus_type {
- const char *name;
- struct bus_attribute *bus_attrs;
- struct device_attribute *dev_attrs;
- struct driver_attribute *drv_attrs;
- int (*match)(struct device *dev, struct device_driver *drv);
- int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
- int (*probe)(struct device *dev);
- int (*remove)(struct device *dev);
- void (*shutdown)(struct device *dev);
- int (*suspend)(struct device *dev, pm_message_t state);
- int (*resume)(struct device *dev);
- const struct dev_pm_ops *pm;
- struct bus_type_private *p;
- };
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同其它组件联系起来的变量。
- struct bus_type_private {
- struct kset subsys;
- struct kset *drivers_kset;
- struct kset *devices_kset;
- struct klist klist_devices;
- struct klist klist_drivers;
- struct blocking_notifier_head bus_notifier;
- unsigned int drivers_autoprobe:1;
- struct bus_type *bus;
- };
- #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都有类似的功能。
- struct bus_attribute {
- struct attribute attr;
- ssize_t (*show)(struct bus_type *bus, char *buf);
- ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
- };
- #define BUS_ATTR(_name, _mode, _show, _store) \
- struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
- #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
struct bus_attribute是bus对struct attribute类型的封装,更方便总线属性的定义。
- static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
- {
- struct bus_attribute *bus_attr = to_bus_attr(attr);
- struct bus_type_private *bus_priv = to_bus(kobj);
- ssize_t ret = 0;
- if (bus_attr->show)
- ret = bus_attr->show(bus_priv->bus, buf);
- return ret;
- }
- static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t count)
- {
- struct bus_attribute *bus_attr = to_bus_attr(attr);
- struct bus_type_private *bus_priv = to_bus(kobj);
- ssize_t ret = 0;
- if (bus_attr->store)
- ret = bus_attr->store(bus_priv->bus, buf, count);
- return ret;
- }
- static struct sysfs_ops bus_sysfs_ops = {
- .show = bus_attr_show,
- .store = bus_attr_store,
- };
- static struct kobj_type bus_ktype = {
- .sysfs_ops = &bus_sysfs_ops,
- };
以上应该是我们最熟悉的部分,bus_ktype中定义了bus对应的kset应该使用的kobj_type实例。与此类似,driver使用的是自定义的driver_ktype,device使用的是自定义的device_ktype。只是这里仅仅定义了sysfs_ops,并未定义release函数,不知bus_type_private打算何时释放。
- int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
- {
- int error;
- if (bus_get(bus)) {
- error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
- bus_put(bus);
- } else
- error = -EINVAL;
- return error;
- }
- void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
- {
- if (bus_get(bus)) {
- sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
- bus_put(bus);
- }
- }
bus_create_file()在bus目录下创建属性文件,bus_remove_file()在bus目录下删除属性文件。类似的函数在driver和device中都有见到。
- static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
- {
- struct kobj_type *ktype = get_ktype(kobj);
- if (ktype == &bus_ktype)
- return 1;
- return 0;
- }
- static struct kset_uevent_ops bus_uevent_ops = {
- .filter = bus_uevent_filter,
- };
- static struct kset *bus_kset;
可以看到这里定义了一个bus_uevent_ops变量,这是kset对uevent事件处理所用的结构,它会用在bus_kset中。
- int __init buses_init(void)
- {
- bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
- if (!bus_kset)
- return -ENOMEM;
- return 0;
- }
在buses_init()中创建了/sys/bus目录,这是一个kset类型,使用了bus_uevent_ops的uevent操作类型。
其实这里的操作不难想象,在devices中我们有一个类似的devices_kset,可以回顾一下。
- static struct kset_uevent_ops device_uevent_ops = {
- .filter = dev_uevent_filter,
- .name = dev_uevent_name,
- .uevent = dev_uevent,
- };
- /* kset to create /sys/devices/ */
- struct kset *devices_kset;
- int __init devices_init(void)
- {
- devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
- ...
- }
- void device_initialize(struct device *dev)
- {
- dev->kobj.kset = devices_kset;
- ...
- }
devices_kset在devices_init()中被创建,使用相应的device_uevent_ops进行uevent处理。而devices_kset又被设为每个device初始化时使用的kset。这就不难想象每个device都是以devices_kset为所属kset的,并使用device_uevent_ops中的处理函数。
只是这里还不知bus_kset会在哪里用到,或许是每个bus所属的kset吧,下面会有答案。
- static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
- {
- return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
- }
- static ssize_t store_drivers_autoprobe(struct bus_type *bus,
- const char *buf, size_t count)
- {
- if (buf[0] == '0')
- bus->p->drivers_autoprobe = 0;
- else
- bus->p->drivers_autoprobe = 1;
- return count;
- }
- static ssize_t store_drivers_probe(struct bus_type *bus,
- const char *buf, size_t count)
- {
- struct device *dev;
- dev = bus_find_device_by_name(bus, NULL, buf);
- if (!dev)
- return -ENODEV;
- if (bus_rescan_devices_helper(dev, NULL) != 0)
- return -EINVAL;
- return count;
- }
- static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
- static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
- show_drivers_autoprobe, store_drivers_autoprobe);
这里定义了总线下的两个属性,只写得drivers_probe,和可读写的drivers_autoprobe。至于其怎么实现的,我们现在还不关心。
- static int add_probe_files(struct bus_type *bus)
- {
- int retval;
- retval = bus_create_file(bus, &bus_attr_drivers_probe);
- if (retval)
- goto out;
- retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
- if (retval)
- bus_remove_file(bus, &bus_attr_drivers_probe);
- out:
- return retval;
- }
- static void remove_probe_files(struct bus_type *bus)
- {
- bus_remove_file(bus, &bus_attr_drivers_autoprobe);
- bus_remove_file(bus, &bus_attr_drivers_probe);
- }
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类型属性进行管理一样。
- static ssize_t bus_uevent_store(struct bus_type *bus,
- const char *buf, size_t count)
- {
- enum kobject_action action;
- if (kobject_action_type(buf, count, &action) == 0)
- kobject_uevent(&bus->p->subsys.kobj, action);
- return count;
- }
- static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
上面定义了bus的一个属性uevent,用于bus所在的kset节点主动发起uevent消息。
同样地uevent文件在driver目录中也有见到。device目录中也有,不过除了store_uevent之外,还增加了show_uevent的功能。
- static struct device *next_device(struct klist_iter *i)
- {
- struct klist_node *n = klist_next(i);
- struct device *dev = NULL;
- struct device_private *dev_prv;
- if (n) {
- dev_prv = to_device_private_bus(n);
- dev = dev_prv->device;
- }
- return dev;
- }
- int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
- if (!bus)
- return -EINVAL;
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error)
- error = fn(dev, data);
- klist_iter_exit(&i);
- return error;
- }
- struct device *bus_find_device(struct bus_type *bus,
- struct device *start, void *data,
- int (*match)(struct device *dev, void *data))
- {
- struct klist_iter i;
- struct device *dev;
- if (!bus)
- return NULL;
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)))
- if (match(dev, data) && get_device(dev))
- break;
- klist_iter_exit(&i);
- return dev;
- }
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的子设备链表的遍历。
- static int match_name(struct device *dev, void *data)
- {
- const char *name = data;
- return sysfs_streq(name, dev_name(dev));
- }
- struct device *bus_find_device_by_name(struct bus_type *bus,
- struct device *start, const char *name)
- {
- return bus_find_device(bus, start, (void *)name, match_name);
- }
bus_find_device_by_name()给出了如何使用遍历函数的例子,寻找bus设备链表中指定名称的设备。
- static struct device_driver *next_driver(struct klist_iter *i)
- {
- struct klist_node *n = klist_next(i);
- struct driver_private *drv_priv;
- if (n) {
- drv_priv = container_of(n, struct driver_private, knode_bus);
- return drv_priv->driver;
- }
- return NULL;
- }
- int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
- void *data, int (*fn)(struct device_driver *, void *))
- {
- struct klist_iter i;
- struct device_driver *drv;
- int error = 0;
- if (!bus)
- return -EINVAL;
- klist_iter_init_node(&bus->p->klist_drivers, &i,
- start ? &start->p->knode_bus : NULL);
- while ((drv = next_driver(&i)) && !error)
- error = fn(drv, data);
- klist_iter_exit(&i);
- return error;
- }
bus_for_each_drv()对bus的驱动链表中的每个驱动调用指定的函数。
这和前面的bus_for_each_dev/bus_find_dev什么都是类似的,只是你可能怀疑为什么会没有bus_find_drv。是没有它的用武之地吗?
请看driver.c中的driver_find()函数。
- struct device_driver *driver_find(const char *name, struct bus_type *bus)
- {
- struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
- struct driver_private *priv;
- if (k) {
- priv = to_driver(k);
- return priv->driver;
- }
- return NULL;
- }
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版本中已经改正了。
- static int bus_add_attrs(struct bus_type *bus)
- {
- int error = 0;
- int i;
- if (bus->bus_attrs) {
- for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
- error = bus_create_file(bus, &bus->bus_attrs[i]);
- if (error)
- goto err;
- }
- }
- done:
- return error;
- err:
- while (--i >= 0)
- bus_remove_file(bus, &bus->bus_attrs[i]);
- goto done;
- }
- static void bus_remove_attrs(struct bus_type *bus)
- {
- int i;
- if (bus->bus_attrs) {
- for (i = 0; attr_name(bus->bus_attrs[i]); i++)
- bus_remove_file(bus, &bus->bus_attrs[i]);
- }
- }
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中,确实添加在完全不同的三个地方。
- static void klist_devices_get(struct klist_node *n)
- {
- struct device_private *dev_prv = to_device_private_bus(n);
- struct device *dev = dev_prv->device;
- get_device(dev);
- }
- static void klist_devices_put(struct klist_node *n)
- {
- struct device_private *dev_prv = to_device_private_bus(n);
- struct device *dev = dev_prv->device;
- put_device(dev);
- }
klist_devices_get()用于bus设备链表上添加节点时增加对相应设备的引用。
klist_devices_put()用于bus设备链表上删除节点时减少对相应设备的引用。
相似的函数是device中的klist_children_get/klist_children_put,这是device的子设备链表。除此之外,bus的驱动链表和driver的设备链表,都没有这种引用计数的保护。原因还未知,也许是linux觉得驱动不太靠谱,万一突然当掉,也不至于影响device的正常管理。
- /**
- * bus_register - register a bus with the system.
- * @bus: bus.
- *
- * Once we have that, we registered the bus with the kobject
- * infrastructure, then register the children subsystems it has:
- * the devices and drivers that belong to the bus.
- */
- int bus_register(struct bus_type *bus)
- {
- int retval;
- struct bus_type_private *priv;
- priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->bus = bus;
- bus->p = priv;
- BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
- retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
- if (retval)
- goto out;
- priv->subsys.kobj.kset = bus_kset;
- priv->subsys.kobj.ktype = &bus_ktype;
- priv->drivers_autoprobe = 1;
- retval = kset_register(&priv->subsys);
- if (retval)
- goto out;
- retval = bus_create_file(bus, &bus_attr_uevent);
- if (retval)
- goto bus_uevent_fail;
- priv->devices_kset = kset_create_and_add("devices", NULL,
- &priv->subsys.kobj);
- if (!priv->devices_kset) {
- retval = -ENOMEM;
- goto bus_devices_fail;
- }
- priv->drivers_kset = kset_create_and_add("drivers", NULL,
- &priv->subsys.kobj);
- if (!priv->drivers_kset) {
- retval = -ENOMEM;
- goto bus_drivers_fail;
- }
- klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
- klist_init(&priv->klist_drivers, NULL, NULL);
- retval = add_probe_files(bus);
- if (retval)
- goto bus_probe_files_fail;
- retval = bus_add_attrs(bus);
- if (retval)
- goto bus_attrs_fail;
- pr_debug("bus: '%s': registered\n", bus->name);
- return 0;
- bus_attrs_fail:
- remove_probe_files(bus);
- bus_probe_files_fail:
- kset_unregister(bus->p->drivers_kset);
- bus_drivers_fail:
- kset_unregister(bus->p->devices_kset);
- bus_devices_fail:
- bus_remove_file(bus, &bus_attr_uevent);
- bus_uevent_fail:
- kset_unregister(&bus->p->subsys);
- kfree(bus->p);
- out:
- bus->p = NULL;
- return retval;
- }
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中,添加相关的子目录和属性文件,就行了。
- void bus_unregister(struct bus_type *bus)
- {
- pr_debug("bus: '%s': unregistering\n", bus->name);
- bus_remove_attrs(bus);
- remove_probe_files(bus);
- kset_unregister(bus->p->drivers_kset);
- kset_unregister(bus->p->devices_kset);
- bus_remove_file(bus, &bus_attr_uevent);
- kset_unregister(&bus->p->subsys);
- kfree(bus->p);
- bus->p = NULL;
- }
bus_unregister()与bus_register()相对,将bus从系统中注销。不过要把bus注销也不是那么简单的,bus中的driver和device都对bus保有一份引用计数。或许正是如此,bus把释放bus->p的动作放在了bus_unregister()中,这至少能保证较早地释放不需要的内存空间。而且在bus引用计数用完时,也不会有任何操作,bus的容错性还是很高的。
- static struct bus_type *bus_get(struct bus_type *bus)
- {
- if (bus) {
- kset_get(&bus->p->subsys);
- return bus;
- }
- return NULL;
- }
- static void bus_put(struct bus_type *bus)
- {
- if (bus)
- kset_put(&bus->p->subsys);
- }
bus_get()增加对bus的引用计数,bus_put()减少对bus的引用计数。实际上这里bus的引用计数降为零时,只是将sysfs中bus对应的目录删除。
无论是bus,还是device,还是driver,都是将主要的注销工作放在相关的unregister中。至于在引用计数降为零时的操作,大概只在device_release()中可见。这主要是因为引用计数,虽然是广泛用在设备驱动模型中,但实际支持的,绝大部分是设备的热插拔,而不是总线或者驱动的热插拔。当然,桥设备的热插拔也可能附带总线的热插拔。
- /*
- * Yes, this forcably breaks the klist abstraction temporarily. It
- * just wants to sort the klist, not change reference counts and
- * take/drop locks rapidly in the process. It does all this while
- * holding the lock for the list, so objects can't otherwise be
- * added/removed while we're swizzling.
- */
- static void device_insertion_sort_klist(struct device *a, struct list_head *list,
- int (*compare)(const struct device *a,
- const struct device *b))
- {
- struct list_head *pos;
- struct klist_node *n;
- struct device_private *dev_prv;
- struct device *b;
- list_for_each(pos, list) {
- n = container_of(pos, struct klist_node, n_node);
- dev_prv = to_device_private_bus(n);
- b = dev_prv->device;
- if (compare(a, b) <= 0) {
- list_move_tail(&a->p->knode_bus.n_node,
- &b->p->knode_bus.n_node);
- return;
- }
- }
- list_move_tail(&a->p->knode_bus.n_node, list);
- }
- void bus_sort_breadthfirst(struct bus_type *bus,
- int (*compare)(const struct device *a,
- const struct device *b))
- {
- LIST_HEAD(sorted_devices);
- struct list_head *pos, *tmp;
- struct klist_node *n;
- struct device_private *dev_prv;
- struct device *dev;
- struct klist *device_klist;
- device_klist = bus_get_device_klist(bus);
- spin_lock(&device_klist->k_lock);
- list_for_each_safe(pos, tmp, &device_klist->k_list) {
- n = container_of(pos, struct klist_node, n_node);
- dev_prv = to_device_private_bus(n);
- dev = dev_prv->device;
- device_insertion_sort_klist(dev, &sorted_devices, compare);
- }
- list_splice(&sorted_devices, &device_klist->k_list);
- spin_unlock(&device_klist->k_lock);
- }
bus_sort_breadthfirst()是将bus的设备链表进行排序,使用指定的比较函数,排成降序。
本节主要分析了bus的注册注销过程,下节我们将深入分析device和driver的绑定过程,了解bus在这其中到底起了什么作用。随着我们了解的逐渐深入,未知的东西也在逐渐增多。但饭要一口一口吃,我们的分析也要一点一点来,急不得。