怎么了选择
分类: LINUX
2012-01-13 11:27:21
在清楚了 kobject 之后,就可以继续分析 device 、 driver 、 bus 了,这三者是设备驱动程序的基本数据结构。
我们可以这样理解,内核用 device 来表示各种设备,然后用 driver 来表示它的驱动,而设备有很多种,也属于相同类型或不同类型,而其对应的驱动可能同时也是另外一个设备的驱动,为了管理这些设备和驱动,就引入了总线 bus_type ,总线上有两个集合(也可以理解为两条链,如上图中的 bus ),分别用来存放该总线类型的设备和驱动,当添加一个设备时就将设备添加到总线的设备集合(图中操作 2 ),同时可能会到驱动集合去匹配适合它的驱动(图中操作 3 ,在此之前 device 和 driver 没有挂钩),如何找到了就会将它们联系起来(图中操作 6 )。同样注册一个驱动,就会把它挂到相应总线类型的驱动集合里(图中操作 1 ),同时去设备集合中找出它锁驱动的设备(图中操作 4 ,在此之前 device 和 driver 没有挂钩),如果找到就把设备链接到它支持的设备链表上(图中操作 6 )。
下面我们进入到代码中去:
struct bus_type 结构定义如下:
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;
};
较之先前一些内核版本, bus_type 把部分私有字段封装到 bus_type_private 类型结构里:
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;
};
字段 klist_devices 和 klist_drivers 分别表示挂在 bus_type 上的驱动和设备链表, bus_type 的其他字段和函数指针将在分析过程中说明。
首先我们要为设备和驱动注册一个总线类型:
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;
这里我们看到了 subsys 用来表示它的文件系统,可以回想上一节 kset 的注册。
这个 bus_kset 是系统启动是创建的,系统 init 进程 kernel_init() 中调用 do_basic_setup() ,其中调用 driver_init(), 其中调用的 buses_init() ,如下
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
从而知道创建的文件系统目录在 /sys/bus 下。
static struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
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;
}
继续 bus_register ()中的代码:
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
bus_create_file ()如下 :
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;
}
用 bus_attr_uevent 创建了 bus->p->subsys.kobj 的属性文件,由上面的赋值知道其读写操作在 bus_ktype 的sysfs_ops ,其定义如下:
static struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
};
static struct sysfs_ops bus_sysfs_ops = {
.show = bus_attr_show,
.store = bus_attr_store,
};
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;
}
由上面的程序可以看出文件的读写操作最终会回到 struct bus_attribute &bus_attr_uevent 的 show 和 store 方法。
bus_attr_uevent 是通过宏定义的:
#define __ATTR(_name,_mode,_show,_store) { /
.attr = {.name = __stringify(_name), .mode = _mode }, /
.show = _show, /
.store = _store, /
}
#define BUS_ATTR(_name, _mode, _show, _store) /
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
其 show 方法为 NULL ,说明不可读。
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;
}
在用户空间可以控制事件的发生 , 如 echo add > event 将产生一个 add 的事件。
接着继续 bus_register ()中的代码:
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;
}
创建两个 kset ,其内嵌 object 的 parent 都指向 priv->subsys.kobj ,说明其文件系统在 bus 所在目录下。由上回分析中知道 kset_create_and_add() 时其内嵌 kobj 的 ktype 指向 kset_ktype ,而这里没有输入参数的 uevent_ops 为NULL ,则会以 priv->subsys.kobj->kset->uevent_ops 来产生事件,我们上面分析中知道这个 uevent_ops 为bus_uevent_ops ,其 filter 会比较 kobj 的 ktype 是不是 &bus_ktype ,而这里是 &kset_ktype ,所以这里是忽略了事件。
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;
add_probe_files() 函数如下:
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;
}
同上面创建 bus_attr_uevent 属性一样创建 bus_attr_drivers_probe 和 bus_attr_drivers_autoprobe 属性文件。粘出属性的代码:
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);
bus_attr_drivers_autoprobe 的 show 指向 NULL ,说明其改文件不可写。
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 ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d/n", bus->p->drivers_autoprobe);
}
在用户空间可以打印 drivers_autoprobe 的值 , 如 cat 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;
}
在用户空间可以改变 drivers_autoprobe 的值 , 如 echo 1 > drivers_autoprobe
继续分析 bus_register ()中的代码:
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
bus_add_attrs() 如下:
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;
}
如果 bus->bus_attrs 存在,则同样为其创建属性文件。
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() 分析完了,总结一下,它注册了一个总线类型,创建对应的文件系统(包括目录和属性),初始化总线上的驱动和设备,这样我们就可以通过内核提供的函数往总线上注册设备和驱动了。
接下一篇文章。