前言
在linux设备驱动模型中,总线可以看作是linux设备模型的核心,系统中的其他设备以及驱动都是以总线为核心围绕。不过驱动程序员在系统中创建一条总线的机会并不多。驱动模型中的总线可以是真是存在的物理总线(USB总线,I2C总线,PCI总线),也可以是为了驱动模型架构设计出的虚拟总线(Platform总线)。为此linux设备驱动模型都将围绕"总线--设备--驱动"来展开,因为符合linux设备驱动模型的设备与驱动都是必须挂载在一个总线上的,无论是实际存在的或者虚拟的。
数据结构
在详细说明bus开始的时候,先需要说明其数据结构,因为一个好的数据结构,就可以很大程度上了解其功能,内核中使用bus_type代表一个总线。
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
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 (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
.name: 总线的名称,比如i2c, spi
.dev_name: 通常用户枚举设备的名称,比如常见的i2c-0, i2c-1....
.dev_root: 该bus默认的父设备。
.dev_attrs: 此bus设备上默认的属性。
.bus_groups, dev_groups, drv_groups: 分别是总线, 设备,驱动的属性。
.match: 当一个设备或者驱动添加到此总线上的时候,bus就会调用match对设备和驱动一一匹配的。
.uevent: 当该bus下的任何设备,驱动发生增加,删除的操作时,就会调用到uevnet函数。
.probe,remove: 当一个driver添加到系统的时候,是先会调用bus的probe的,如果bus的probe都没有初始化,则bus下的任何驱动或者设备都无法使用。但是不是所有的bus都会实现probe函数的,虚拟的bus是不存在初始化的。
.supend,resume: bus电源管理回调函数。
.pm: 一组电源管理的回调函数。
p: 一个用来管理总线上设备与驱动的数据结构。
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
struct kset glue_dirs;
struct class *class;
};
.subsys: 用来表示bus所在的子系统,系统中所有注册的bus都将指向bus_kset。
.devices_kset: 代表该bus下所有设备的集合。
.drivers_kset: 代表该bus下所有驱动的集合。
.interfaces: 用于保存该bus下所有的interface。
.klist_devices: 代表该bus下所有设备的链表。
.klist_drivers: 代表该bus下所有驱动的链表。
.bus_notifier: 该bus创建的通知链。
.drivers_autoprobe: 代表在该bus下注册某一个驱动或者设备的时候,是否需要自动匹配
.bus: 指向所属的bus。
.class: 指向所属的class。
BUS相关的函数
- buses_init(创建bus/system集合)
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
if (!system_kset)
return -ENOMEM;
return 0;
}
通过上述的操作将在sysfs下创建了一个名字为"bus"的目录,同时也会在/sys/devices/下创建一个名字为"system"的目录。
在前面kset学习中知道,当这个名为bus的kset下有状态变化的时候,就会发送uevent消息的,在发送过程中就会调用到bus_uevent_ops中实现的函数。
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 const struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
在bus_uevent_ops中只实现了filter函数。此函数只要是判断发生状态的kobj对象类型是不是总线类型,不是就不会上报该事件的。
- bus_register(注册一个总线到系统中)
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); //分配subsys_private结构体
if (!priv)
return -ENOMEM;
priv->bus = bus; //设置bus指针
bus->p = priv; //设置bus->p指针
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); //初始化bus通知链
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //设置该bus的名字,体现在/sys/bus/下
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset; //设置该bus所属的kset, 以及ktype
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys); //注册该kset到系统中,表现在/sys/bus下
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent); //创建该bus的uevent属性
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj); //在该bus下创建devices目录
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj); //在该bus下创建drivers目录
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces); //初始化interface, mutex, klist_devices, klist_drivers
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus); //创建bus的probe属性
if (retval)
goto bus_probe_files_fail;
retval = bus_add_groups(bus, bus->bus_groups); //创建bus的属性
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
}
比如常见的通过bus_register创建一个i2c总线,就会在/sys/bus/下创建i2c目录, 以及devices目录, driver目录, 以及probe属性, uevent属性。
root@test:/sys/bus/i2c # ls -l
drwxr-xr-x root root 2012-01-01 08:17 devices
drwxr-xr-x root root 2012-01-01 08:17 drivers
-rw-r--r-- root root 4096 2012-01-01 08:17 drivers_autoprobe
--w------- root root 4096 2012-01-01 08:17 drivers_probe
--w------- root root 4096 2012-01-01 08:17 uevent
而在devices下就是该i2c-bus下所有的设备,drivers下就是i2c-bus下所有去的驱动。
- subsys_register(注册一个子系统)
static int subsys_register(struct bus_type *subsys,
const struct attribute_group **groups,
struct kobject *parent_of_root)
{
struct device *dev;
int err;
err = bus_register(subsys); //注册一个总线
if (err < 0)
return err;
dev = kzalloc(sizeof(struct device), GFP_KERNEL); //分配一个device结构,然后设置名字
if (!dev) {
err = -ENOMEM;
goto err_dev;
}
err = dev_set_name(dev, "%s", subsys->name);
if (err < 0)
goto err_name;
dev->kobj.parent = parent_of_root; //设置该device的parent,通常都是在buses_init中的system_kset
dev->groups = groups; //表现在sys中就是在/sys/devices/system下新建一个subsys->name的目录
dev->release = system_root_device_release;
err = device_register(dev); //注册该设备
if (err < 0)
goto err_dev_reg;
subsys->dev_root = dev;
return 0;
}
- subsys_system_register(/sys/devices/system/下注册一个子系统)
int subsys_system_register(struct bus_type *subsys,
const struct attribute_group **groups)
{
return subsys_register(subsys, groups, &system_kset->kobj);
}
该函数将会调用上面的subsys_register函数。举例:
static int __init clockevents_init_sysfs(void)
{
int err = subsys_system_register(&clockevents_subsys, NULL);
if (!err)
err = tick_init_sysfs();
return err;
}
struct bus_type clockevents_subsys = {
.name = "clockevents",
.dev_name = "clockevent",
};
这样的话,就会在/sys/devices/system下创建一个名字为"clockevent"设备,在/sys/bus/下创建一个名字为“clockevents”的总线
root@test:/sys/bus/clockevents # ls
devices
drivers
drivers_autoprobe
drivers_probe
uevent
root@test:/sys/devices/system/clockevents # ls -l
drwxr-xr-x root root 2012-01-01 08:00 broadcast
drwxr-xr-x root root 2012-01-01 08:00 clockevent0
drwxr-xr-x root root 2012-01-01 08:00 clockevent1
drwxr-xr-x root root 2012-01-01 08:00 clockevent2
drwxr-xr-x root root 2012-01-01 08:00 clockevent3
drwxr-xr-x root root 2012-01-01 08:00 clockevent4
drwxr-xr-x root root 2012-01-01 08:00 clockevent5
drwxr-xr-x root root 2012-01-01 08:00 clockevent6
drwxr-xr-x root root 2012-01-01 08:00 clockevent7
drwxr-xr-x root root 2012-01-01 08:00 power
-rw-r--r-- root root 4096 2012-01-01 08:00 uevent
不过根据该函数的注释“Do not use this interface for anything new, it exists for compatibility with bad ideas only.” 意思是不在建议使用该函数了。
- subsys_virtual_register(在/sys/devices/virtual/下创建一个子系统)
int subsys_virtual_register(struct bus_type *subsys,
const struct attribute_group **groups)
{
struct kobject *virtual_dir;
virtual_dir = virtual_device_parent(NULL);
if (!virtual_dir)
return -ENOMEM;
return subsys_register(subsys, groups, virtual_dir);
}
中间会调用virtual_device_parent函数
struct kobject *virtual_device_parent(struct device *dev)
{
static struct kobject *virtual_dir = NULL;
if (!virtual_dir)
virtual_dir = kobject_create_and_add("virtual",
&devices_kset->kobj);
return virtual_dir;
}
也就是在/sys/devices/下创建一个名字为“virtual”的目录。