linux设备驱动——andriod平台wlan驱动II——给军爷的答案

转自 : http://wsqhs.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&_c=BlogPart&partqs=amonth%3d4%26ayear%3d2009

 

 

这仍然是摘自自己工作笔记的第二部分,主要记述了我对在内核中注册驱动程序(我们这里以我的SDIO总线及wlan网卡举例)后,在内核中调用了哪些内核函数?其相应的设备树是如何形成的?以及我看《 linux设备驱动第三版》linux设备模型章节的诸多疑惑的总结。仍然要强调一遍,这里仅仅是我的理解而已,正确与否我无法判断。所以对军爷问题的回答,也希望军爷和我多多探讨。
    首先我要做的是总领一下 kobject、kset、subsystem这三个结构之间的关系。同样以下内容摘自我的工作笔记,但顺序颠倒了,这里先行介绍可能对我们之后的代码介绍要有些许帮助。
    关于 kobject 结构,首先 每个目录代表一个 kobject 对象,每个文件代表 kobject 的属性(这里我实在不敢肯定!)。kobject 是组成设备模型的基本结构,它只是一个类似 c++ 中基类的东西。
   我想给出这样一个比喻(我不知道这个比喻是否恰当):对于每一个目录,它们都有一些共同的特点,比如都有名字,都有父子目录。而对于“方法”来说,它们都有用来实现引用计数的方法。我们将这些共同点封装起来形成了基类。但是这个基类并不能真正的代表一个目录,加上了其它的特征之后才能成为一个真正的目录,所以我们需要将 kobject 这个结构嵌入所有的 sysfs 下的目录中以获得 kobject 提供的一些特征,在加上每个目录自己所独有特征从而形成一个 sysfs 中的目录。这也就是我理解的kobject结构的意义。那么kobject结构是否真的就对应于一个目录呢?回答是肯定的,因为我在代码中找到了这样的答案,每一次调用kobject_add函数,都会在这个函数中调用create_dir来创建一个目录。
    那么kset又是什么呢?为什么需要kset?还有subsystem呢?kobject、kset、subsystem这三个结构到底在设备树中各自承担什么样的角色? kset 是是嵌入相同类型结构的 kobject 集合,对于c++来说意味着将继承自所有基类的子类放在一起,这有什么意义呢?这样想是否就会觉得很合理了,kset是用来将所有有着共同特点的目录联系在一起的东西。可能你还是要在内心不停的问,这太抽象了 太抽象了,能举个例子吗?是的,比如我们的设备树下的pci总线目录/sys/bus/pci下挂接着很多的设备和驱动(当然有很多的设备和驱动可以挂着在pci总线下),那么我们如何将它们联系在一起呢?是的,是通过kset结构,那么kset结构是个什么样的地位呢?噢,我想之前我理解错了,它不是/sys/bus/pci目录,也不是/sys/bus/pci/drivers目录中的某个具体驱动,它刚好就是/sys/bus/pci/drivers目录。不相信? 那么打开你们的linux操作系统,看一看是否每一条总线都有着drivesdevices两个目录?它们两个都是嵌入了kset结构;那么/sys/bus/pci/drivers目录中的某个具体驱动呢?它就是嵌入了kobject结构的目录;/sys/bus/pci目录呢?猜对了,它就是subsystem结构的嵌入。 还有要补充的吗?是呢,现在subsystem貌似被取消了,取代它的就是kset结构。这样也就是说一系列的kset就组成了subsystem
    我似乎忘记了自己问了三个问题,对的,我还需要问答自己看《linux设备驱动第三版》提出来的第三个问题:为什么需要kset?提出这个问题的想法很幼稚,难道我们不能够只用kobject结构将挂接在一条总线上的驱动或者设备都链接在一起吗?只要我们有list_head!linux内核的作者回答了我这个问题,你需要一个容器来管理它们,就像c++中的容器一样。但这至少说明了kset并不是必要的存在。那么kset是如何扮演容器的角色的呢?这个问题,我们需要看后面的图解来回答。
    以上是摘自我穿插在工作笔记中的关于kobject、ket、subsystem三个结构的描述,可能大家读完这段解释仍然无法形成具体的概念,仍然觉得很抽象。没关系,因为我在代码分析之后还会有总结性的举例。那时还不明白我就没辙了!!!
    好了,现在我们得费劲心思的捋一遍我们的驱动注册代码,以便找到设备树添加的关键部分。我想我又得强调一下,我的介绍是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 
      4driver_create_file创建文件属性,会生成一个属性为driver_attr_uevent的属性文件。
      5add_bind_files生成属性为driver_attr_unbinddriver_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;想象一下我们折腾了那么长时间的kobjectkset的意义,是的,这里就是将driverkobj所属的kset挂接上总线的kset。我们这里显得很绕对吗?幸运的是我在网上找到了一个非常棒的示意图:
 
                     
 

   我该怎么样来形容这个图呢? 它把依赖关系说的已经足够清楚了!!我这里唯一要解释的仅仅是在驱动文件夹被正确后所谓的文件属性有在那里。同样我们顺着目录/sys/bus/sdio/driver/wlan_sdio/下我们发现了bindunbindnew_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;

}

你可能感兴趣的:(linux设备驱动——andriod平台wlan驱动II——给军爷的答案)