您的位置:首页 > 运维架构 > Linux

linux设备树的建立过程

2016-04-01 13:06 453 查看
http://blog.csdn.net/duqi_2009/article/details/17161163

为了阐明表示总线、设备和设备驱动程序的各个数据结构之间彼此的关联,它们的注册过程是很有必要的。顺序一定是如下:(1)注册总线---bus_register;(2)注册设备device_register;(3)注册设备驱动程序----bus_add_driver。

下文摘自:点击打开链接

现在我们得费劲心思的捋一遍我们的驱动注册代码,以便找到设备树添加的关键部分。我想我又得强调一下,我的介绍是SDIO驱动,所以请大家看着 linux内核代码drivers/mmc中关于sdio的驱动来理解我下面的笔记中的内容(想不看内核代码就理解设备树,我想太难太难)

有关 sbi_register 函数(这是在我的wlan驱动代码中的函数,并不需要你太多的关注 )中sdio_register_driver函数(从现在开始就都是内核函数了) 注册驱动的介绍,在 sdio_register_driver 中将会指明驱动的名称(这里是 ”wlan_sdio ” ),此函数的参数为 sdio_driver 结构。 驱动所挂接的总线 sdio_bus_type ,其结构类型为:

static struct bus_type sdio_bus_type = {

.name = "sdio", // 总线类型

.dev_attrs = sdio_dev_attrs, // 属性

.match = sdio_bus_match, //ops

.uevent = sdio_bus_uevent,

.probe = sdio_bus_probe,

.remove = sdio_bus_remove,

};

这个结构将在 sdio_register_driver 函数中被赋值以产生 device_driver 结构。也就是说 device_driver 被包含在 sdio_driver 中。 随后调用函数 driver_register ,其参数为 device_driver (此结构中定义了 bus_type,也就是驱动挂接的总线类型 )。至此将转入所有驱动(不止是 sdio 卡驱动)的注册代码中。此时的驱动结构已经变为device_drive ( 内核定义的驱动结构 ) 。

driver_register 将会完成挂接驱动至总线及生成设备树的过程,其完成的任务大致包括:

1 、设置设备驱动中 kobject 的名字

2 、将 kobject 添加至 sysfs 中,也就是在 sysfs 树中创建一个目录( kobject 中有一个函数 create_dir 用于创建目录。)

3 、调用 driver_attach 函数完成 probe

4 、 driver_create_file 创建文件属性,会生成一个属性为 driver_attr_uevent 的属性文件。

5 、 add_bind_files 生成属性为 driver_attr_unbind 和 driver_attr_bind 的属性文件,关于文件的属性,它定义了文件的读写方式。

如此抽象的描述及流水账般的记述我发现还是很没有说服力,因此我决定给出一个具体的例子,并给出代码的实现。先看例子——让我们看一下 /sys/bus/ 下的目录,然后来个具体的描述:已知我们的总线的目录名字为”sdio” 。也就是说在 /sys/bus 目录下有一个目录叫 sdio ,即 /sys/bus/sdio 。它是怎么形成的?内核中有”sdio” 总线的驱动,找到这个函数 bus_register (&sdio_bus_type ) ;就是用来注册总线的。我们在前面不也看到了 driver_register 函数吗,是的,你猜对了,还有一个函数叫 device_register 。在这个函数调用完成后,就会得到 /sys/bus/ 目录下的 sdio 目录了。那么驱动的名字 ”wlan_sdio” 又是如何插入到/sys/bus/drivers/wlan_sdio 目录下的呢。在 driver_register->driver_register->bus_add_driver 函数中有个重要的语句 drv->kobj.kset
= &bus->drivers ; 想象一下我们折腾了那么长时间的 kobject 与 kset 的意义,是的,这里就是将 driver 的 kobj 所属的 kset 挂接上总线的 kset 。我们这里显得很绕对吗?幸运的是我在网上找到了一个非常棒的示意图:





我该怎么样来形容这个图呢? 它把依赖关系说的已经足够清楚了!!我这里唯一要解释的仅仅是在驱动文件夹被正确后所谓的文件属性有在那里。同样我们顺着目录 /sys/bus/sdio/driver/wlan_sdio/ 下我们发现了 bind 、unbind 和 new_id 三个文件。
好了,别忘了我们在前面提过的问题,kset 是如何扮演容器的角色的呢?图中很清楚吧,看看粉红色的箭头,kset children list用来将将同类型的kobject连接起来以达到容器的效果。
但是我们的驱动还没有结束,关于这个图的建立,我们势必要用代码才能说得明白。
我们还是花费一点时间来看一下内核中的代码,关于 sdio 总线注册的代码部分,其它的部分,大家类举就可以了。懂了这段自然就懂了全部:
int bus_register( struct bus_type
* bus)
{
int retval;

BLOCKING_INIT_NOTIFIER_HEAD (&bus->bus_notifier);

retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name); // 总线的名字 ”sdio”, 我们说过了一个 kobject对应一个目录,这时会为这个目录赋值名字。
if (retval)
goto out;

bus->subsys.kobj.kset = &bus_subsys; // 将其 kset 指向 bus_subsys., 如何理解? 看看ldd_bus_type 指向 bus_subsys 的那条蓝线

retval = subsystem_register(&bus->subsys); //bus->subsys 的注册,实际上是用 kset 指针将其链接在一起。好吧 我得承认实际上这里取消了 subsysem 结构的概念,用 kset 代替了。这里会创建一个目录。它是一个 kset 也是一个 kobject,因为 kset 包含了 kobject 。
if (retval)
goto out;

retval = bus_create_file(bus, &bus_attr_uevent); // 创建属性文件
if (retval)
goto bus_uevent_fail;

kobject_set_name(&bus->devices.kobj, "devices"); // 设置 devices
kset 的名字为 devices
bus->devices.kobj.parent = &bus->subsys.kobj; // 参见 ldd_bus_type->devices 指向 ldd_bus_type->sub_sys的红色箭头(注意这里也不是 subsystem, 而是 kset )
retval = kset_register(&bus->devices); // 创建 devices 命名的目录
if (retval)
goto bus_devices_fail;

kobject_set_name(&bus->drivers.kobj, "drivers"); // 设置 devices
kset 的名字为 drivers
bus->drivers.kobj.parent = &bus->subsys.kobj; // 同样参见 ldd_bus_type->drivers 指向 ldd_bus_type->sub_sys的红色箭头(注意这里也不是 subsystem, 而是 kset )。
bus->drivers.ktype = &driver_ktype; // 对 kobject 默认属性的赋值
retval = kset_register(&bus->drivers); // 创建 drivers 命名的目录
if (retval)
goto bus_drivers_fail;

klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); //klist 结构的初始化,关于 klist 链接的作用我们已经说得很清楚了
klist_init(&bus->klist_drivers, NULL, NULL);

bus->drivers_autoprobe = 1;
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 type '%s' registered/n", bus->name);
return 0;

bus_attrs_fail :
remove_probe_files(bus);
bus_probe_files_fail :
kset_unregister(&bus->drivers);
bus_drivers_fail :
kset_unregister(&bus->devices);
bus_devices_fail :
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail :
subsystem_unregister(&bus->subsys);
out :
return retval;
}
至此 我们终于理顺了整个过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: