SDIO_WIFI之我对WIFI了解
WIFI 分析
SDIO_WIFI分析WIFI之我见
作者:黄树新
时间:2011-11-18
地点:华清远见深圳分中心
SDIO_WIFI整个设备分为两个部分,一个是SD卡,一个是WIFI。SD卡部分主要涉及的重点在与如何识别SD卡和支持热拔插,而WIFI部分主要的重点在于发送和接收数据,现在,由小弟带着大家走一遭,本文涉及内容完全属于个人对WIFI部分理解,属于个人观点,如有错误,欢迎指导纠正。
WIFI驱动属于网络设备驱动,那么我们首先从它的结构出发,要了解它的结构,我们首先了解一般网络设备驱动的结构。
下图1是LINUX下网络驱动程序的体系结构:
数据包发送 数据包接收 网络协议接口层 dev_queue_xmit() netif_rx()
网络设备接口层 struct net_device
数据包发送 中断处理(数据包网络驱动功能层 hard_start_xmit() 接收)
网络设备与媒介层 网络物理设备媒介
图1
1、网络协议接口层想网络层协议提供一个统一的数据包收发接口,不论是
上层协议为APP还是IP,都通过dev_queue_xmit()函数发送数据,并通
过netif_rx()函数接收数据。这一层的存在使得上层协议独立于具体的
设备。
2、网络设备接口层向协议接口层提供统一的用于描述具体网络设备属性和
操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。
实际上,网络设备接口层从宏观上规划了具体操作硬件的设备驱动功能
层的结构。
3、设备驱动功能层函数是网络设备接口层net_device数据结构的具体成
- 1 -
WIFI 分析
员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit()
函数启动发送操作,并通过网络设备上的中断触发接收操作。
4、网络设备与媒介层是完成数据包发送和接收的物理实体,包括网络适配
器和具体的传输媒介,网络适配器被设备驱动层中得函数物理上的驱动。
对于LINUX系统来说,网络设备和媒介可以是虚拟的。
在
具体的网络设备驱动程序时,我们需要完成的主要工作是编写设备驱
动功能层的相关函数以填充net_device数据结构的内容并将net_device注册入
内核。
在进入正式驱动之前,还是要给大家补充一点基础知识,就是如何管理总线,
设备,驱动之间关系的,关于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
- 2 -
WIFI 分析
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; //设备属性
- 3 -
WIFI 分析
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就是容器了)。
首先不管是设备还是驱动,都是挂接在某条总线上的,也就是说我们根据总线类型的不同来区分各种设备和驱动。(但是我们也要注意到,一个设备和驱动是可以挂接在不同的总线上的,比如网卡可以挂接在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指针指向这个驱动
- 4 -
WIFI 分析 的结构,同时将这个设备的device结构挂接在device_driver结构中的klist链表中。 另外要提及一点就是我居然发现在device结构中也发现了一个这样的结构struct klist_node knode_driver,它看起来跟klist有关,但是我得说我确实不认为device需要一个klist来挂上它能使用的driver。同样,当一个设备被注册时,它也会去寻找挂接在同一条总线上的驱动,并将自己与这个驱动联系起来。
关于基础知识大致就这么些,如果需要知道的更细,请致电10086。
现在,结合代码叙述注册设备,发现设备(对于驱动来说这里的工作其实很少,如果想知道内核做了些什么,就请跟着我一起看看内核的奥秘),以及数据传输的驱动代码。sdio_register_driver(&sdio) 函数被调用从而注册sdio驱动.这里已经进入内核部分代码,他存在于内核的/drivers/net/wireless/libertas /If_sdio.c文件中,特别说明,我用的是LINUX 2.6.38的内核。
首先我们看看注册函数:
int sdio_register_driver(struct sdio_driver *drv)
{
drv->drv.name = drv->name; //首先忽略下面两行,直接进入driver_register函数.
drv->drv.bus = &sdio_bus_type; //实际上这行代码是关键,告诉内核这个设备是
sdio_bus_type。而下面的函数中我们要找的仅仅
是调用probe函数的地方而已,稍后分析
return driver_register(&drv->drv);
}
大家来看driver_register函数的内容,由于其中涉及较多有关klist、kobject结构的内容,如果有不明白的地方,请查看《设备驱动开发详解 第2版》,因为有些我也不是很明白。
接着我们再进入driver_register(&drv->drv)函数:
int driver_register(struct device_driver *drv)
{
int ret;
struct device_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'
needs updating - please use ""bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus); /在kobject结构组成的链表中查找是否已
经存在这个驱动 ,驱动必然挂接在某个
总线上,返回值是device_driver结构的指
针,返回后再提示驱动已经安装。
if (other) {
put_driver(other); //由于之前增加了引用计数,这里在减1
- 5 -
WIFI 分析
printk(KERN_ERR "Error: Driver '%s'
is already registered, ""aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);//进来(把驱动加载到总线上,此处为sdio_bus_type)
//此函数是重点~
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
//加载驱动到相应的组里,成功返回非0,不成功返回0 ?(不明白)
if (ret)
bus_remove_driver(drv); //删除驱动 ? ?(不明白)
return ret;
}
接着跟进bus_add_driver(drv)函数,它的作用是:如果驱动还未挂接在总线上,挂接它并且调用probe函数进行探测。
看看重点代码:
int bus_add_driver(struct device_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函数中,很简单的一句话:
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;
if (!driver_match_device(drv, dev))
- 6 -
WIFI 分析
/*调用driver_match_device匹配
如果匹配成功,则向下执行,否则原路返回*/
return 0;
if (dev->parent) /* Needed for USB */
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);
return 0;
}
接着我们再探秘driver_probe_device(drv, dev)部分源码: int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))//判断驱动是否加载
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %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);
return ret;
}
准备到最后了,大家请忍耐,我们进去really_probe(dev, drv)看看: static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
.
.
.
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
- 7 -
WIFI 分析
ret = drv->probe(dev); //我们看到了这里会调用probe函数,就是我们驱动里
面的那个相应的函数。.probe= if_sdio_probe
.
.
.
}
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函数。
__driver_attach函数.实际上就是查找device结构.
error=fn(dev,data);
klist_iter_exit(&i);
return error;
}
probe函数的调用,就取决于你在你注册的device结构中挂接的总线的类型了.因为调用 dev->bus->probe(dev); 所以清查看一下你注册是挂接的总线的probe函数的参数即可. 一般来说,参数会是两个,因为一类总线上总是可以挂接多个设备,所以我们还需要一个device_id. 如果行到else部分: else if (drv->probe) .这里调用驱动的probe函数,由于我们注册的是sdio_driver结构.看一看sdio_driver结构:
struct sdio_driver {
char *name;
const struct sdio_device_id *id_table;
int (*probe)(struct sdio_func *, const struct sdio_device_id *);
void (*remove)(struct sdio_func *);
struct device_driver drv;
};
好了,大家跟我进去那么久,该出来歇息歇息了,现在我就把大概的注册框图贴出来,方便了解,图2
- 8 -
WIFI 分析
- 9 -
WIFI 分析
图2
总的一句话就是,我们已经完成了设备驱动的注册,注册最后又返回执行if_sdio_probe()函数,那么,我们跟着if_sdio_probe()函数看看(部分代码):
static int if_sdio_probe(struct sdio_func *func,const struct sdio_device_id *id)
{
struct mmc_host *host = func->card->host;
//分配了一个描述mmc_host的结构体
/*
struct mmc_host {
struct device *parent;
struct device class_dev;
int index;
const struct mmc_host_ops *ops;
.
.
.
}*/
switch (card->model) {
case MODEL_8385:
card->scratch_reg = IF_SDIO_SCRATCH_OLD;
break;
case MODEL_8686:
card->scratch_reg = IF_SDIO_SCRATCH; //选择wifi的类型 我们用的是8686
break;
case MODEL_8688:
default: /* for newer chipsets */
card->scratch_reg = IF_SDIO_FW_STATUS;
break;
}
/*初始化一个工作队列,延迟执行*/
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
//初步猜测if_sdio_host_to_card_worker的作用是启动固件 发送使用
//接着就调用if_sdio_host_to_card 发送使用
sdio_claim_host(func);/*检测当前是否被占用*/
ret = sdio_enable_func(func); //使能sdio函数(可能跟SD卡驱动有关系)
if (ret)
goto release;
- 10 -
WIFI 分析
ret = sdio_claim_irq(func, if_sdio_interrupt);
//当接收到数据时会产生这个中断,中断函数if_sdio_interrupt(后面单独分析)
priv = lbs_add_card(card, &func->dev);
priv->hw_host_to_card = if_sdio_host_to_card;
//当执行主线程priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");
//调用lbs_thread()后就会进入回调函数if_sdio_host_to_card(发送时用)
priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
ret = lbs_start_card(priv);//有东西看 请进please
}
***********************分析sdio_claim_irq(func, if_sdio_interrupt)****************
1、 ret = sdio_card_irq_get(func->card);
2、 host->sdio_irq_thread =kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
mmc_hostname(host));
//进入sdio_irq_thread
3、 ret = process_sdio_pending_irqs(host->card);
4、 终于进入我们想知道的函数中:
static int process_sdio_pending_irqs(struct mmc_card *card)
{
//遍历读取相关寄存器的数据,如果有中断,则返回执行驱动的中断处理函数
count = 0;
for (i = 1; i <= 7; i++) {
if (pending & (1 << i)) {
struct sdio_func *func = card->sdio_func[i - 1];
if (!func) {
printk(KERN_WARNING "%s: pending IRQ for "
"non-existant function\n",
mmc_card_id(card));
ret = -EINVAL;
} else if (func->irq_handler) {
func->irq_handler(func);//调用SD卡驱动的中断处理函数
count++;
} else {
}
}
***************************sdio_claim_irq分析完毕*******************
- 11 -
WIFI 分析
*********************** lbs_start_card(priv)分析*********************** struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
{
/*调用函数alloc_netdev()函数分配了一个mesh_dev网络设备*/
dev = alloc_netdev(0, "wlan%d", ether_setup);
if (!dev) {
dev_err(dmdev, "no memory for network device instance\n");
goto err_adapter;
}
/*设置了该网卡设备的操作函数,如设置MAC地址等*/
dev->netdev_ops = &lbs_netdev_ops;
lbs_deb_thread("Starting main thread...\n");
init_waitqueue_head(&priv->waitq);
priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");
//进入线程lbs_thread 这个线程被添加到prv->waitq队列中,然后休眠
//只有被唤醒才会被执行
/*在main.c 和 mesh.c (不知道是那个)都注册了
.ndo_start_xmit= lbs_hard_start_xmit
当有数据发送时,执行dev_queue_xmit()函数,接着调用
lbs_hard_start_xmit()函数
唤醒主线程priv->main_thread(),进入lbs_thread()函数*/
INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);
//这个工作队列很让人纠结,据说是实现无线网卡的热点扫描,跟进去里面太
复杂了,差点出不来
}
此处执行完毕会进入线程lbs_thread,让我们接着进去:
static int lbs_thread(void *data) {
//处理待处理的命令
lbs_process_command_response(priv,
priv->resp_buf[resp_idx],
priv->resp_len[resp_idx]);
spin_lock_irq(&priv->driver_lock);
//处理硬件事件 如拔卡,在Cmdresp.c中处理命令
lbs_process_event(priv, event);
spin_lock_irq(&priv->driver_lock);
//当有数据发送时,执行此函数发送数据
- 12 -
WIFI 分析
int ret = priv->hw_host_to_card(priv, MVMS_DAT,
priv->tx_pending_buf,
priv->tx_pending_len);
}
到此为止,bs_start_card(priv)分析完毕,估计看代码有点吃力,那么我就贴
张美图给大家看看吧。图3
图3
这会感觉好多了吧。
******************************bs_start_card(priv)分析结束*************************
***************************** lbs_start_card(priv)分析*****************************
int lbs_start_card(struct lbs_private *priv)
{
/* poke the firmware */
//刺探固件 估计是安装那两个 helper_sd.bin sd8686.bin固件
ret = lbs_setup_firmware(priv);
- 13 -
WIFI 分析
if (ret)
goto done;
lbs_update_channel(priv);//?
lbs_init_mesh(priv);//进来
lbs_debugfs_init_one(priv, dev);//?
}
进入lbs_init_mesh(priv):
int lbs_init_mesh(struct lbs_private *priv) {
if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
(priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
/* 10.0.0.pXX new firmwares should succeed with TLV
* 0x100+37; Do not invoke command with old TLV.
*/
priv->mesh_tlv = TLV_TYPE_MESH_ID;
if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
priv->channel))
//配置mesh
priv->mesh_tlv = 0;
}
if (priv->mesh_tlv) {
sprintf(priv->mesh_ssid, "mesh");
priv->mesh_ssid_len = 4;
lbs_add_mesh(priv);//再添加mesh
if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
lbs_pr_err("cannot register lbs_mesh attribute\n");
ret = 1;
}
}
接着进入bs_add_mesh(priv):
int lbs_add_mesh(struct lbs_private *priv) {
//郁闷 分配网络设备 接下来ether_setup()就对它各种赋值
mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
if (!mesh_dev) {
lbs_deb_mesh("init mshX device failed\n");
- 14 -
WIFI 分析
ret = -ENOMEM;
goto done;
}
/* Register virtual mesh interface */
//终于走到终点站(天后)向VFS注册网络设备
这样应用层才可以实现对网络设备的操作
ret = register_netdev(mesh_dev); //对网络设备的注册
if (ret) {
lbs_pr_err("cannot register mshX virtual interface\n");
goto err_free;
}
}
关于网络设备的注册,小弟跟进去了下:
?err = register_netdevice(dev)
?ret = netdev_register_kobject(dev) ?error = device_add(dev)
?error = device_private_init(dev) ?bus_probe_device(dev);
?ret = device_attach(dev)
最后:ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
原来它跟一般的平台Driver注册是一样,我。。。
************************* lbs_start_card(priv)分析完毕*********************
故事发展到这一拍,基本上已经把if_sdio_probe函数的故事讲完毕。那么来总一个小结:
在这个if_sdio_probe()函数主要做了两件事(这也是我们的最终目的):
1、调用函数alloc_netdev()函数分配(mesh_dev = alloc_netdev(0, "msh%d",
ether_setup),)了一个mesh_dev网络设备。
2、对网络设备mesh_dev进行初始化,操作函数绑定之后,接着调用了register_netdev(mesh_dev)函数,将网络设备注入到内核。
到这里可以在系统跑起来后用ifconfig命令来查看这个网络设备,还可以设置这个网络设备的ip地址,网关,子网掩码等。
在这个probe函数中还创建了一个内核线程,用来管理这个网络设备的数据发送、事件的处理(卡的拔出)和一些命令的处理。还申请了一个接收数据中断,只要这个wifi模块接收到数据,就会发送一个中断,告诉sdi host我接收到了数据,然后,host就会发送读取命令,对其进行读取,将读取到的数据放到skbuf中递交给ip层。
- 15 -
WIFI 分析 下面贴出整个if_sdio_probe函数的整体框架,欢迎指导:图4
图4
既然是属于网卡,那么它肯定具备发送和接收数据的功能,现在我们一一解说.
- 16 -
WIFI 分析
数据接收:
在上面的if_sdio_probe函数中我们已经申请了一个接收数据的中断if_sdio_interrupt(),当接收到数据,wifi模块通过产生中断来提醒host去读取数据,host通过发送cmd53命令将数据接收后放到skbuf中,然后调用ip层提供的接口(netif_rx())函数将数据递交到ip层。这里在数据的读取上,我只是到request这个函数,这个函数是在host的platformdriver中绑定的。在下面具体将分析到request()函数。
查看if_sdio_interrupt()源码:
static void if_sdio_interrupt(struct sdio_func *func)
{
card = sdio_get_drvdata(func);//返回设备的数据
cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret);//获取中断号
lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause);
if (cause & IF_SDIO_H_INT_UPLD) {
ret = if_sdio_card_to_host(card);//进入(接收时用)
if (ret)
goto out;
}
}
进入if_sdio_card_to_host(card)分析:
static int if_sdio_card_to_host(struct if_sdio_card *card)
{
//读取数据 放在card->buff中 (接收时用)
ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
if (ret)
goto out;
switch (type) {
case MVMS_CMD:
ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);
- 17 -
WIFI 分析
if (ret)
goto out;
break;
case MVMS_DAT:
//处理数据
ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);
if (ret)
goto out;
break;
}
**********sdio_readsb(card->func, card->buffer, card->ioport, chunk)分析*****************
int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,int count)
{
return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count); }
进入sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count):
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,unsigned addr, int incr_addr,
u8 *buf, unsigned size)
{
//进入去取数据 //接收和发送时都用
ret = mmc_io_rw_extended(func->card, write,
func->num, addr, incr_addr, buf,
blocks, func->cur_blksize);
if (ret)
return ret;
}
进入mmc_io_rw_extended(func->card, write,func->num, addr, incr_addr,
buf,blocks, func->cur_blksize);再进入:mmc_wait_for_req(card->host, &mrq),再进
入:mmc_start_request(host, mrq),里面就会有我们想要找的
- 18 -
WIFI 分析
host->ops->request(host, mrq);//这个走的路线太远了,找不到走哪里了,发送和接
收都跑这里来了。
******************************** dio_readsb()分析完毕****************************
************ ****************** if_sdio_handle_data(card, card->buffer + 4, chunk - 4)分析
static int if_sdio_handle_data(struct if_sdio_card *card,u8 *buffer, unsigned size)
{
skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN);
skb_reserve(skb, NET_IP_ALIGN);
data = skb_put(skb, size);
memcpy(data, buffer, size);
lbs_process_rxed_packet(card->priv, skb);//接收数据包 进入
}
进入lbs_process_rxed_packet(card->priv, skb),我们会在里面找到对应的
netif_rx(skb);//提交数据到协议层 这个函数时提供给网络接口层的。到此,分析
完毕。
**************************** if_sdio_handle_data()分析完毕*************************
到了这里,我们接收数据的所有分析已经结束,还是按照惯例,贴一张图出
来方便了解。
图5
- 19 -
WIFI 分析
发送数据:
ip层通过接口(dev_queue_xmit())将数据递交给网络设备协议接口层,网络设备借口层利用netdevice中的注册函数的数据发送函数(.ndo_start_xmit
= lbs_hard_start_xmit,),唤醒主线程priv->main_thread,在主线程中回调(priv->hw_host_to_card = if_sdio_host_to_card;)if_sdio_host_to_card()函数。紧接着唤醒packet_work(包处理队列),最后通过sdio协议将数据发送到wifi模块的sram中,wifi模块会自动的将对数据进行处理(例如DA转换等)同无线电波的形式将数据发送到无线路由器。
好了。我们开始从int dev_queue_xmit(struct sk_buff *skb)开始:发送函数,找了好久都没找到到底是哪里调用它的。
在dev_queue_xmit(struct sk_buff *skb)里进入rc = dev_hard_start_xmit(skb,
dev, txq),在dev_hard_start_xmit()进入rc = ops->ndo_start_xmit(skb, dev),此处会进入(Main.c和Mesh.c中都定义有,但是都会进入同一个函数)的
- 20 -
WIFI 分析
lbs_hard_start_xmit(),在此函数中wake_up(&priv->waitq),唤醒主线程的
lbs_thread,跳到main.c。在lbs_thread()函数中int ret = priv->hw_host_to_card(priv,
MVMS_DAT,priv->tx_pending_buf,priv->tx_pending_len)进入下一层,此处比较诡
异,执行后会跳到驱动if_sdio_probe注册的函数(priv->hw_host_to_card = if_sdio_host_to_card);进入if_sdio_host_to_card()函数:
static int if_sdio_host_to_card(struct lbs_private *priv,u8 type, u8 *buf, u16 nb)
{
memcpy(packet->buffer + 4, buf, nb);
//把buf数据copy到packet->buffer 发送时用
queue_work(card->workqueue, &card->packet_worker);
//唤醒延迟工作队列执行if_sdio_probe中 INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
//执行后进入if_sdio_host_to_card_worker()函数
}
进入if_sdio_host_to_card_worker()函数,在里面进入ret =
sdio_writesb(card->func, card->ioport,下一层进入return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count),最后接下来的操作跟接收数据
的一样,这里就不要啰嗦了。还是老规矩,贴图~图6
- 21 -
WIFI 分析
感谢大家不厌其烦的跟我走了一个下午,到了这里,SDIO_WIFI无线网卡WIFI部分分析完毕,感谢~好人一生平安~
- 22 -