SDIOWIFI之我对WIFI了解.docx
- 文档编号:10135219
- 上传时间:2023-02-08
- 格式:DOCX
- 页数:29
- 大小:493.12KB
SDIOWIFI之我对WIFI了解.docx
《SDIOWIFI之我对WIFI了解.docx》由会员分享,可在线阅读,更多相关《SDIOWIFI之我对WIFI了解.docx(29页珍藏版)》请在冰豆网上搜索。
SDIOWIFI之我对WIFI了解
SDIO_WIFI分析WIFI之我见
作者:
黄树新
时间:
2011-11-18
地点:
华清远见深圳分中心
SDIO_WIFI整个设备分为两个部分,一个是SD卡,一个是WIFI。
SD卡部分主要涉及的重点在与如何识别SD卡和支持热拔插,而WIFI部分主要的重点在于发送和接收数据,现在,由小弟带着大家走一遭,本文涉及内容完全属于个人对WIFI部分理解,属于个人观点,如有错误,欢迎指导纠正。
WIFI驱动属于网络设备驱动,那么我们首先从它的结构出发,要了解它的结构,我们首先了解一般网络设备驱动的结构。
下图1是LINUX下网络驱动程序的体系结构:
图1
1、网络协议接口层想网络层协议提供一个统一的数据包收发接口,不论是上层协议为APP还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。
这一层的存在使得上层协议独立于具体的设备。
2、网络设备接口层向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。
实际上,网络设备接口层从宏观上规划了具体操作硬件的设备驱动功能层的结构。
3、设备驱动功能层函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接收操作。
4、网络设备与媒介层是完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动层中得函数物理上的驱动。
对于LINUX系统来说,网络设备和媒介可以是虚拟的。
在设计具体的网络设备驱动程序时,我们需要完成的主要工作是编写设备驱动功能层的相关函数以填充net_device数据结构的内容并将net_device注册入内核。
在进入正式驱动之前,还是要给大家补充一点基础知识,就是如何管理总线,设备,驱动之间关系的,关于bus_type、device_driver、device这三个内核结构,在内核代码中可以找到。
由于这三个结构的重要性,我们在这里先将它们贴出来,我会用红色的注释标注。
我在笔记中将会提到的一些结构成员以其代表的意义,这样便于对照。
(我不得不承认,这三个结构的代码枯燥复杂。
如果我误导了你,请指正我,所以我的建议是不妨先看完了笔记再来看这些结构)。
1、设备结构的定义:
structdevice{
structklistklist_children;
structklist_nodeknode_parent;/*nodeinsiblinglist*/
structklist_nodeknode_driver;
structklist_nodeknode_bus;
structdevice*parent;
structkobjectkobj;//kobject结构,关于这个结构与kset结构以及subsystem结构,笔记中会有描述。
charbus_id[BUS_ID_SIZE];/*positiononparentbus*/
structdevice_type*type;
unsignedis_registered:
1;
unsigneduevent_suppress:
1;
structsemaphoresem;/*semaphoretosynchronizecallsto*itsdriver.*/
structbus_type*bus;/*typeofbusdeviceison//这个设备挂接的总线的类型
structdevice_driver*driver;/*whichdriverhasallocatedthisdevice*/
//这个设备挂接的驱动
void*driver_data;/*dataprivatetothedriver*/
void*platform_data;/*Platformspecificdata,devicecoredoesn'ttouchit*/
structdev_pm_infopower;
#ifdefCONFIG_NUMA
intnuma_node;/*NUMAnodethisdeviceiscloseto*/
#endif
u64*dma_mask;/*dmamask(ifdma'abledevice)*/
u64coherent_dma_mask;/*Likedma_mask,butfor
alloc_coherentmappingsas
notallhardwaresupports
64bitaddressesforconsistent
allocationssuchdescriptors.*/
structlist_headdma_pools;/*dmapools(ifdma'ble)*/
structdma_coherent_mem*dma_mem;/*internalforcoherentmemoverride*/
/*archspecificadditions*/
structdev_archdataarchdata;
spinlock_tdevres_lock;
structlist_headdevres_head;
/*class_devicemigrationpath*/
structlist_headnode;
structclass*class;
dev_tdevt;/*dev_t,createsthesysfs"dev"*/
structattribute_group**groups;/*optionalgroups*/
void(*release)(structdevice*dev);
};
2、设备驱动的结构:
structdevice_driver{
constchar*name;//设备驱动的名字
structbus_type*bus;//设备驱动挂接的总线的类型
structkobjectkobj;//kobject结构
structklistklist_devices;//这个驱动对应的设备的链表
structklist_nodeknode_bus;
structmodule*owner;
constchar*mod_name;/*usedforbuilt-inmodules*/
structmodule_kobject*mkobj;
int(*probe)(structdevice*dev);
int(*remove)(structdevice*dev);
void(*shutdown)(structdevice*dev);
int(*suspend)(structdevice*dev,pm_message_tstate);
int(*resume)(structdevice*dev);
};
3、总线结构:
structbus_type{
constchar*name;//总线的名字
structmodule*owner;
structksetsubsys;//与该总线相关的subsystem
structksetdrivers;//挂接在该总线上的驱动的集合
structksetdevices;//挂接在该总线上的设备的集合
structklistklist_devices;
structklistklist_drivers;
structblocking_notifier_headbus_notifier;
structbus_attribute*bus_attrs;//总线属性
structdevice_attribute*dev_attrs;//设备属性
structdriver_attribute*drv_attrs;//驱动属性
int(*match)(structdevice*dev,structdevice_driver*drv);
int(*uevent)(structdevice*dev,structkobj_uevent_env*env);
int(*probe)(structdevice*dev);
int(*remove)(structdevice*dev);
void(*shutdown)(structdevice*dev);
int(*suspend)(structdevice*dev,pm_message_tstate);
int(*suspend_late)(structdevice*dev,pm_message_tstate);
int(*resume_early)(structdevice*dev);
int(*resume)(structdevice*dev);
unsignedintdrivers_autoprobe:
1;
};
我们注意到只有在bus_type结构中有kset结构,其他两个结构中则没有,我们知道kset结构是用于存放相同类型的kobject的,这究竟是个什么意思呢?
kset又是为什么而存在的呢?
为什么不能就是kobject呢?
(关于kobject结构,我们很难抽象的形容,尽管它就是一个抽象的概念,我们将留待看代码的时候介绍,这里可以将kobject看成一个基类,kset就是容器了)。
首先不管是设备还是驱动,都是挂接在某条总线上的,也就是说我们根据总线类型的不同来区分各种设备和驱动。
(但是我们也要注意到,一个设备和驱动是可以挂接在不同的总线上的,比如网卡可以挂接在pci和sdio总线上,但这并不是说在linux设备模型中就可以同时挂接在两个总线上,我们只能选择其中的一种挂接)。
在内核代码中我们找到了bus_type结构,我们发现了它使用了三个kset结构,分别是:
structksetsubsys、structksetdrivers、structksetdevices。
我们先抛开subsys因为它是用来向上挂接的。
这里我们先看drivers与devices两个kset结构的意义。
我们从发现一个设备或者驱动说起吧,一个设备或者驱动向内核注册的时候(对于设备来说就是被插入了;对于驱动来说就是.ko模块被加载了),对于每一次设备注册或者驱动注册,我们都得分配一个device结构或者device_drive结构,每一次我们都需要将device结构挂入drivers或devices(kset结构)链表中,这样我们能通过总线找到挂接在这个总线上的所有设备和驱动。
但是这里我们注意到仅仅将设备们和驱动们挂接在总线上并不能表明设备和驱动之间的关系,这样的处理仅仅表明了驱动、设备与总线的关系,它们申明了我现在挂接在这条总线下,以后操作我就请通过这条总线。
那么设备如何认出驱动,或者驱动如何认出设备呢?
是的,我们是使用的probe函数。
那么需要完成哪些过程了?
在这些结构总是如何关联的?
这才是我们关心的问题。
所以这里我们将不得不在说明一下klist结构的作用。
在内核代码中我们再次找到了device结构和device_drive结构。
我们注意到在device结构中存在一个structdevice_driver*driver这样的声明,而在device_drive中却并没有同样的包含device结构。
我们这样想就明白了:
对于一个设备来说,我们只能绑定一个驱动;而对于一个驱动来说,我们是可以对应于多个设备的。
也就是说这里device中的driver指针将会指向其绑定的驱动。
那么回到probe探测函数,在我们对一个设备驱动进行注册的过程中,我们会在其相应的总线(也就是其挂接的总线)上发出一个探测,这个探测会搜寻所有挂接在这个总线上的尚未被绑定的设备(也就是driver指针为NULL),然后将driver指针指向这个驱动的结构,同时将这个设备的device结构挂接在device_driver结构中的klist链表中。
另外要提及一点就是我居然发现在device结构中也发现了一个这样的结构structklist_nodeknode_driver,它看起来跟klist有关,但是我得说我确实不认为device需要一个klist来挂上它能使用的driver。
同样,当一个设备被注册时,它也会去寻找挂接在同一条总线上的驱动,并将自己与这个驱动联系起来。
关于基础知识大致就这么些,如果需要知道的更细,请致电10086。
现在,结合代码叙述注册设备,发现设备(对于驱动来说这里的工作其实很少,如果想知道内核做了些什么,就请跟着我一起看看内核的奥秘),以及数据传输的驱动代码。
sdio_register_driver(&sdio)函数被调用从而注册sdio驱动.这里已经进入内核部分代码,他存在于内核的/drivers/net/wireless/libertas/If_sdio.c文件中,特别说明,我用的是LINUX2.6.38的内核。
首先我们看看注册函数:
intsdio_register_driver(structsdio_driver*drv)
{
drv->drv.name=drv->name;//首先忽略下面两行,直接进入driver_register函数.
drv->drv.bus=&sdio_bus_type;//实际上这行代码是关键,告诉内核这个设备是sdio_bus_type。
而下面的函数中我们要找的仅仅是调用probe函数的地方而已,稍后分析
returndriver_register(&drv->drv);
}
大家来看driver_register函数的内容,由于其中涉及较多有关klist、kobject结构的内容,如果有不明白的地方,请查看《设备驱动开发详解第2版》,因为有些我也不是很明白。
接着我们再进入driver_register(&drv->drv)函数:
intdriver_register(structdevice_driver*drv)
{
intret;
structdevice_driver*other;
BUG_ON(!
drv->bus->p);
if((drv->bus->probe&&drv->probe)||
(drv->bus->remove&&drv->remove)||
(drv->bus->shutdown&&drv->shutdown))
printk(KERN_WARNING"Driver'%s'
needsupdating-pleaseuse""bus_typemethods\n",drv->name);
other=driver_find(drv->name,drv->bus);/在kobject结构组成的链表中查找是否已经存在这个驱动,驱动必然挂接在某个总线上,返回值是device_driver结构的指针,返回后再提示驱动已经安装。
if(other){
put_driver(other);//由于之前增加了引用计数,这里在减1
printk(KERN_ERR"Error:
Driver'%s'
isalreadyregistered,""aborting...\n",drv->name);
return-EBUSY;
}
ret=bus_add_driver(drv);//进来(把驱动加载到总线上,此处为sdio_bus_type)
//此函数是重点!
if(ret)
returnret;
ret=driver_add_groups(drv,drv->groups);
//加载驱动到相应的组里,成功返回非0,不成功返回0?
(不明白)
if(ret)
bus_remove_driver(drv);//删除驱动?
?
(不明白)
returnret;
}
接着跟进bus_add_driver(drv)函数,它的作用是:
如果驱动还未挂接在总线上,挂接它并且调用probe函数进行探测。
看看重点代码:
intbus_add_driver(structdevice_driver*drv)
{
.
.
.
error=driver_attach(drv);
/*在driver_attach()函数里,会进行driver和device匹配
,无论匹配成功与否都会原路返回,
接着向下执行klist_add_tail(&priv->knode_bus,&bus->p->klist_drivers);
语句,这条语句的作用就是将if_sdio_driver添加到klist_drivers上。
*/
.
.
.
}
driver_attach函数中,很简单的一句话:
intdriver_attach(structdevice_driver*drv)
{
returnbus_for_each_dev(drv->bus,NULL,drv,__driver_attach);
}
这个函数会调用__driver_attach函数,我们已经接近目标了
staticint__driver_attach(structdevice*dev,void*data)
{
structdevice_driver*drv=data;
if(!
driver_match_device(drv,dev))
/*调用driver_match_device匹配
如果匹配成功,则向下执行,否则原路返回*/
return0;
if(dev->parent)/*NeededforUSB*/
device_lock(dev->parent);
device_lock(dev);
if(!
dev->driver)
driver_probe_device(drv,dev);//走这里噢这就是我们要找的入口
device_unlock(dev);
if(dev->parent)
device_unlock(dev->parent);
return0;
}
接着我们再探秘driver_probe_device(drv,dev)部分源码:
intdriver_probe_device(structdevice_driver*drv,structdevice*dev)
{
intret=0;
if(!
device_is_registered(dev))//判断驱动是否加载
return-ENODEV;
pr_debug("bus:
'%s':
%s:
matcheddevice%swithdriver%s\n",
drv->bus->name,__func__,dev_name(dev),drv->name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret=really_probe(dev,drv);//走这里噢
pm_runtime_put_sync(dev);
returnret;
}
准备到最后了,大家请忍耐,我们进去really_probe(dev,drv)看看:
staticintreally_probe(structdevice*dev,structdevice_driver*drv)
{
intret=0;
.
.
.
if(dev->bus->probe){
ret=dev->bus->probe(dev);
if(ret)
gotoprobe_failed;
}elseif(drv->probe){
ret=drv->probe(dev);//我们看到了这里会调用probe函数,就是我们驱动里面的那个相应的函数。
.probe=if_sdio_probe
.
.
.
}
if(dev->bus->probe)这个表示是否注册了device结构,如果注册了并且给device结构挂接上了驱动和总线,那么调用挂接在device结构中的总线的probe函数。
这里的device结构从哪里冒出来的?
它在bus_for_each_dev函数中:
intbus_for_each_dev(structbus_type*bus,structdevice*start,
void*data,int(*fn)(structdevice*,void*))
{
structklist_iteri;
structdevice*dev;
interror=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函数。
__driver_attach函数.实际上就是查找device结构.
error=fn(dev,data);
klist_iter_exit(&i);
returnerror;
}
probe函数的调用,就取决于你在你注册的device结构中挂接的总线的类型了.因为调用dev->bus->probe(dev);所以清查看一下你注册是挂接的总线的probe函数的参数即可.一般来说,参数会是两个,因为一类总线上总是可以挂接多个设备,所以我们还需要一个device_id.如
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- SDIOWIFI WIFI 了解