现在的位置: 首页 > 综合 > 正文

linux设备驱动—— andriod平台wlan驱动

2013年12月08日 ⁄ 综合 ⁄ 共 13436字 ⁄ 字号 评论关闭

关于这一部分的blog,所有的内容均摘自自己的工作总结笔记,在很多网站都发现了自己写的技术总结的转载感到很高兴,虽然我还是个菜鸟,但是
我会继续努力。另外关于wlan驱动这方面的资料真的很少,我基本上是靠自己读代码来理解那些繁琐的寄存器读写、802.11/e/h/d等标准的,真的
比较辛苦。不过好在算是慢慢的搞清楚了这个流程,在此之前我们仍然要补习一下关于在2.6版本内核中写驱动的知识。

   
有关linux设备模型这一块比较复杂,我不敢断定自己理解的肯定正确,但是我会在做这个驱动的过程中回过头来修改自己的笔记并且纠正自己在blog上贴
的并不正确的地方。另外,我的无线网卡是挂接在SDIO总线上的,所以呢,我们之前会先介绍一点SDIO的驱动,当然并不在这篇blog上,这篇blog
会是总领性的关于基础知识的介绍。下面是笔记:
    在进入正式的驱动代码之前,我们不得不补充一点基础知识,也就是

2.6

版本内核的现在,内核是如何管理总线,驱动,设备之间的关系的,关于

bus_type


device_driver


device

这三个内核结构在内核代码中可以找到。由于这三个结构的重要性,我们在这里先将它们贴出来,
我会用红色的注释标注。我在笔记中将会提到的一些结构成员以其代表的意义,这样便于对照。(我不得不承认,这三个结构的代码枯燥复杂,但我可以保证,在看完我总结的笔记之后,你会对这三个结构中重要的数据成员有个非常不错的了
解,当然你得对内核和驱动有基本的了解。如果我误导了你,请指正我,所以我的建议是不妨先看完了笔记再来看这些结构)



  1、设备结构的定义:

struct
device {
 struct

klist  klist_children;        
 struct
klist_node knode_parent;  
/*
node in sibling list */

 struct
klist_node knode_driver;
 struct
klist_node knode_bus;
 struct
device  *parent;        


 struct
kobject kobj;            //kobject
结构,关于这个结构与kset结构以及subsystem结构,笔记中会有描述。


 char
 bus_id[BUS_ID_SIZE
]; /*
position on parent bus */


 struct
device_type *type;
 unsigned
  is_registered:1;
 unsigned
  uevent_suppress:1;


 struct
semaphore sem; 

/* semaphore to synchronize calls to
     
* its driver.
      */



 struct
bus_type * bus;  /* type of bus
device is on */                 


 //这个设备挂接的总线的类型

 struct
device_driver *driver; /* which driver has allocated this
device */     


//这个设备挂接的驱动

 void
  *driver_data; /* data private to the driver */

 void
  *platform_data; /* Platform specific data, device core
doesn't touch it */


 struct
dev_pm_info power;


#ifdef
CONFIG_NUMA


 int
  numa_node; /* NUMA node this device is close to */

#endif

 u64
  *dma_mask; /* dma mask (if dma'able device) */

 u64 
 coherent_dma_mask;/

* Like dma_mask,
but for
          alloc_coherent mappings as
          not all
hardware supports
          64 bit addresses for consistent
         
allocations such descriptors. */



 struct
list_head dma_pools; /* dma pools
(if dma'ble) */



 struct
dma_coherent_mem *dma_mem;

/* internal for coherent mem override
*/

 /* arch specific
additions */


 struct
dev_archdata archdata;


 spinlock_t  devres_lock;
 struct
list_head devres_head;


 /*
class_device migration path */


 struct
list_head node;
 struct
class  *class;
 dev_t
   devt;  /*
dev_t, creates the sysfs "dev" */


 struct
attribute_group **groups; /* optional groups */


 void
 (*release)(struct
device * dev);
};


  2、设备驱动的结构:

struct
device_driver {
 const char
  *
name;                                        //设备驱动的名字

 struct
bus_type  * bus;                                   
//设备驱动挂接的总线的类型


 struct
kobject  kobj;                                      //kobject结构

 struct

klist  klist_devices;                              
//这个驱动
对应的设备的链表

 struct
klist_node knode_bus;


 struct
module  * owner;
 const char
  * mod_name; /* used for
built-in modules */


 struct

module_kobject * mkobj;


 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);
};


 
  3、总线结构:

struct
bus_type {
 const
char 
 *
name;                                 

//总线的名字

 struct
module  * owner;


 struct
kset  subsys;                                //与该总线相关的subsystem

 struct

kset  drivers;                              
 //挂接在
该总线上的驱动的集合

 struct
kset  devices;                               //挂接在该总线上的设备的集合

 struct
klist  klist_devices;                        
 struct
klist  klist_drivers;


 struct
blocking_notifier_head bus_notifier;


 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
(*suspend_late)(struct

device * dev, pm_message_t state);
 int
(*resume_early)(struct
device * dev);
 int

(*resume)(struct
device *
dev);

 unsigned
int

drivers_autoprobe:1;
};


   我们注意到只有在
bus_type

结构中有
kset

结构,其他两个结构中则没有,我们知道
kset

结构是用于存放相同类型的
kobject

的,这究竟是个什么意思呢?
kset

又是为什么而存在的呢?为什么不能就是
kobject

呢?(关于kobject结构,我们很难抽象的形容,尽管它就是一个抽象的概念,我们将留待看代码的
时候介绍,这里可以将kobject看成一个基类,kset就是容器了),下面我会按照自己的理解来回答这个问题(建议通读一下《

linux

设备驱动第三版》的
linux

设备模型章节)

  
首先不管是设备还是驱动,都是挂接在某条总线上的,也就是说我们根据总线类型的不同来区分各种设备和
驱动。(但是我们也要注意到,一个设备和驱动是可以挂接在不同的总线上的,比如网卡可以挂接在

pci


sdio

总线上,但这并不是说在
linux

设备模型中就可以同时挂接在两个总线上,我们只能选择其中的一种挂接)。

  
在内核代码中我们找到了
bus_type

结构,我们发现了它使用了三个
kset

结构,分别是:
       struct kset  subsys 


struct kset  drivers


struct kset devices

。我们先抛开
subsys

因为它是用来向上挂接的。这里我们先看
drivers


devices

两个
kset

结构的意义。我们从发现一个设备或者驱动说起吧,一个设备或者驱动向内核注册的时候(对于设备来说就
是被插入了;对于驱动来说就是

.ko

模块被加载了),对于每一
次设备注册或者驱动注册,我们都得分配一个

device

结构或

device_drive

结构,每一次我们都需要将
device

结构挂入
drivers


devices(kset

结构
)

链表中,这样我们能通过总线找到挂接在这个总线上的所有设备和驱动。但是这里我们注意到仅仅将设备们
和驱动们挂接在总线上并不能表明设备和驱动之间的关系,这样的处理仅仅表明了驱动、设备与总线的关系,它们申明了我现在挂接在这条总线下,以后操作我就请
通过这条总线。


   那么设备如何认出驱动,或者驱动如何认出设备呢?是的,我们是使用的



probe


函数。

那么需要完成哪些过程
了?在这些结构总是如何关联的?这才是我们关心的问题。所以这里我们将不得不在说明一下

klist
结构的作用。在内核代码中我们再次找到了
device
结构和
device_drive
结构。我们注意到在
device
结构中存在一个
struct
device_driver *driver

这样的声明,而在
device_drive
中却并没有同样的包含
device
结构。我们这样想就明白了:对于一个设备来说,我们只能绑
定一个驱动;而对于一个驱动来说,我们是可以对应于多个设备的。


也就是说这里
device

中的
driver
指针将会指向其绑定的驱动。那么回到
probe
探测函数,在我们对一个设备驱动进行注册的过程中,我们会
在其相应的总线(也就是其挂接的总线)上发出一个探测,这个探测会搜寻所有挂接在这个总线上的尚未被绑定的设备(也就是

driver
指针为
NULL
),然后将
driver
指针指向这个驱动的结构,同时将这个设备的
device
结构挂接在
device_driver
结构中的
klist
链表中。

另外要提及一点就是我居然发现在
device
结构中也发现了一个这样的结构
struct
klist_node    knode_driver

,它看起来跟
klist
有关,但是我得说我确实不认为
device
需要一个
klist
来挂上它能使用的
driver
。同样,当一个设备被注册时,它也会去寻找挂接在同一条总
线上的驱动,并将自己与这个驱动联系起来。


 
  
关于基础知识大致就这么些,如果需要知道的更细,可以查看《linux设备驱动第三版》,后面的我们会结合代码叙述注册设备,发现设备(对于驱动来说这里
的工作其实很少,如果想知道内核做了些什么,就请跟着我一起看看内核的奥秘),以及数据传输的驱动代码。


sdio_register_driver(&sdio)


函数被调用从而注册
sdio驱动.这里已经进入内核部分代码,他存在于内核的


drivers/mmc/core/sdio_bus.c

文件
中,噢,忘了说了,我看的内核代码版本是2.6.30.1.
    贴一下




sdio_register_driver
函数:   


 int
sdio_register_driver(struct
sdio_driver *drv)
{
       
drv->drv.name = drv->name;                          //首先忽略下面两行,直接进入





driver_register
函数





.
        drv->drv.bus =
&sdio_bus_type;                      //实际上这行代码是关键,而下面的函数中我们要找的仅仅是调用probe函数的地方而已,稍后分析

        return
driver_register(&drv->drv);





}




来看


driver_register
函数的内容,由于其中涉及较多有关klist、kobject结构的内容,如果有不明白的地
方,请查看同为《




linux设备驱动——andriod平台wlan驱动》系列的介绍klist、kobject内容的章节,这里希望有的放矢.它的代码在


drivers/base/driver.c中.






int
driver_register(struct
device_driver *drv)
{
       
int
ret;
        struct
device_driver *other;

       
if ((drv->bus->probe && drv->probe) ||
           
(drv->bus->remove && drv->remove) ||
           
(drv->bus->shutdown && drv->shutdown))
              
 
printk(KERN_WARNING "Driver '%s' needs updating - please use "
                       
"bus_type methods/n", drv->name);

        other =
driver_find(drv->name, drv->bus);        //在kobject

构组成的链表中查找是否已经存在这个驱动,以前的blog讲过,驱动必然挂接在某个总线上.请复习,返回值是





device_driver

结构的指针

        if (other) {
               
put_driver(other);               
//由于之前增加了引用计数,这里在减1

                      
               


printk(KERN_ERR "Error: Driver '%s' is already registered, "
                       
"aborting.../n", drv->name);


                return -EEXIST
;
        }

       
ret =
bus_add_driver(drv);                                          //此函数是重点!


       
if (ret)
                return

ret;
        ret = driver_add_groups(drv,
drv->groups);              
//这两个函数不用介绍也猜的出来


        if (ret)
               
bus_remove_driver(drv);
       
return

ret;
}
那么我们接着看



bus_add_driver
函数





,这个函数的作用
是:如果驱动还未挂接在总线上,挂接它并且调用probe函数进行探测.

它的代码在


drivers/base/bus.c中.






int
bus_add_driver(struct
device_driver
*drv)            
//这个函数我不记得我的blog中是否有讲过(我记得我写过注释,如果没写过,就请自己看吧  呵呵 )


{
      
.......................................
        if
(drv->bus->p->drivers_autoprobe) {
                error =
driver_attach(drv);                           //这个函数是重点.


               
if (error)
                        goto out_unregister;
        }
 
      ......................................      
 



}

driver_attach

数在

drivers/base/dd.c





,很简单的一句话:


 int
driver_attach(struct
device_driver *drv)
{
        return
bus_for_each_dev(drv->bus,
NULL, drv, __driver_attach
);
}


这个函数会调用__driver_attach

数,我们已经接近目标了.



static
int

__driver_attach(struct
device *dev, void
*data)
{
       
struct
device_driver *drv
= data;

       
/*
         * Lock device and try to
bind to it. We drop the error
         * here and always return 0,
because we need to keep trying
         * to bind to devices and some
drivers will return an error
         * simply if it didn't support
the device.
         *
         * driver_probe_device() will spit a
warning if there
         * is an error.
         */

       
if (dev->parent)        /*
Needed for USB */


               
down(&dev->parent->sem);
        down(&dev->sem);
       
if (!dev->driver)
                driver_probe_device(drv, dev);                     
//此函数就是我们要找的函数了.


        up(&dev->sem);
        if
(dev->parent)
                up(&dev->parent->sem);

        return
0;
}


driver_probe_device
函数中有一个

really_probe
函数,这是我们的最终目的地


:
static int
really_probe(struct
device *dev, struct
device_driver *drv)
{
       
...................................................
           if
(dev->bus->probe) {                                         //我们看到了这里会调用probe函数,同样这里也会决定你的probe函数是使用
一个参数还是两个参数,


                ret = dev->bus->probe(dev);
                          因为这取决于你是如何注册的.也就是是否调用了(仅仅针对于网友的提问)
platform_device_register

               
if (ret)                                                               
函数来注册设备.具体看下面的总结.

                       
goto probe_failed;
        } else if (drv->probe) {
                ret = drv->probe(dev);

               
if (ret)
                        goto probe_failed;
        }
        
....................................................
}


好了,这里来总结一下得失,同时根据以上列的代码回答网友的提问: if (dev->bus->probe)
这个表示是否注册
了device结构,如果注册了并且给device结构挂接上了驱动和总线,那么调用挂接在device结构中的总线的probe函数.这里的
device结构从哪里冒出来的?它在

bus_for_each_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->knode_bus :
NULL

));
        while ((dev = next_device(&i)) &&
!error)                                      //查找每个挂接在sdio总线上的设备,看他们是否有注册,并调用相应的probe函数也就是

               
error = fn(dev,
data);                                                               


__driver_attach
函数.实际上就是查找device结构


.
       
klist_iter_exit(&i);





        return
error;
}


关于platform_device结构与device结构的关系,就不用我在解释了
吧!probe函数的调用,就取决于你在你注册的device结构中挂接的总线的类型了.因为调用


dev->bus->probe(dev); 所以清查看一下你注册是挂接的总线的probe函数的参数即可.



一般来说,参数会是两个,因为一类总线上总是可以挂接多个设备,所以我们还需要一个device_id.

如果行到else部分

: else if
(drv->probe) .这里调用驱动的probe函数,由于我们注册的是

sdio_driver
结构



.看一看sdio_driver结构的申明,在include/linux
/mmc/sdio_func.h中:





struct sdio_driver {
       
char *name;
        const struct sdio_device_id *id_table;

        int (*probe)(struct sdio_func *, const
struct sdio_device_id *);                     //很显然probe就是两个参数,而不是一个.


       
void (*remove)(struct sdio_func *);

        struct device_driver
drv;
};

至于为什么你的probe是一个参
数,我希望你能给出完整的注册流程才能分析,但是我认为根据我刚才分析的流程应该可以自己找出相应的代码了.





////////////////////////////////////////
首先我要做的是总领一下kobject、kset、
subsystem

这三个结构之间的关系。同样以下内容摘自我的工作笔记,但顺序颠倒了,这里先行介绍可能对我们之后的代码介绍要有些许帮
助。

    关于
kobject

结构,首先
每个目录代表一个
kobject

对象,每个文件代表
kobject

的属性(这里我实在不敢肯定
!)。kobject

是组成设备模型的基本结构,它只是一个类似
c++
中基类的东西。
  
我想给出这样一个比喻(我不知道这个比喻是否恰当):对于每一个目录,它们都有一些共同的特点,比如都有名字,都有父子目录。而对于“方法”来说,它们都
有用来实现引用计数的方法。我们将这些共同点封装起来形成了基类。但是这个基类并不能真正的代表一个目录,加上了其它的特征之后才能成为一个真正的目录,
所以我们需要将

kobject

这个结构嵌入所有的
sysfs

下的目录中以获得
kobjec
t

提供的一些特征,在加上每个目录自己所独有特征从而形成一个
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
操作系统,看一看是否每一条总线
都有着drives
devices
两个目录?它们两个都是嵌入了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 

      4


driver_create_file

创建文件属性,会生成一个属性为
driver_attr_uevent

的属性文件。


      5


add_bind_files

生成属性为
driver_attr_unbind


driver_attr_bind

的属性文件,关于文件的属性,它定义了文件的读写方式。

抱歉!评论已关闭.