BCMwifi驱动学习
1、代码路径:Z:\home\stonechen\svn\TD550_X\TD550\3rdparty\wifi\BCM43362\special\bcmdhd\dhd\sys\dhd_linux.c
#if LINUX_VERSION_CODE >=KERNEL_VERSION(2, 6, 0)
late_initcall(#dhd_module_init);
#else
module_init(dhd_module_init);
#endif
module_exit(dhd_module_cleanup);
以上代码是wifi驱动的入口函数!!!!
static int __initdhd_module_init(void)
{
int error = 0;
DHD_TRACE(("%s: Enter\n",__FUNCTION__));
dhd_msg_level=0xffff;
wl_android_init(); //设置wlan的名字
。。。。。。。。
/* Call customer gpio to turn onpower with WL_REG_ON signal */
dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON);//开启wlan的电源
注:此函数调用如下!!!
caseWLAN_POWER_ON:
WL_TRACE(("%s:call customer specific GPIO to turn on WL_REG_ON\n",
__FUNCTION__));
#ifdefCUSTOMER_HW
bcm_wlan_power_on(1);//这里将调用customize下的board_cfg.c
/*Lets customer power to get stable */
OSL_DELAY(200);
#endif/* CUSTOMER_HW */
voidbcm_wlan_power_on(int mode)
{
gpio_request(90,"gpio90");
gpio_direction_output(90,0);
mdelay(10);
gpio_direction_output(90,1);
printk("bcm_wlan_power_on1\n");
mdelay(200);
#if1
if(mode==1)
{
printk("bcm_wlan_power_on2\n");
SP_force_scan_sdio(1);
//bcm_detect_card(0);
printk("bcm_wlan_power_on3, start mmc_detect_change\n");
}
#endif
//setOOB_Wake up Source for WIFI. /*GPIO142*/
//__raw_bits_or(BIT_14,SPRD_GPIO_BASE+0x0380+0X18); /*Michael*/
}
EXPORT_SYMBOL(bcm_wlan_power_on);
#ifdefined(CONFIG_WIFI_CONTROL_FUNC)
if (wl_android_wifictrl_func_add()< 0) //
goto fail_1;
#endif
#if (LINUX_VERSION_CODE >=KERNEL_VERSION(2, 6, 27))
sema_init(&dhd_registration_sem,0);
#endif /* (LINUX_VERSION_CODE >=KERNEL_VERSION(2, 6, 27)) */
error = dhd_bus_register();
if (!error)
printf("\n%s\n",dhd_version);
else {
DHD_ERROR(("%s:sdio_register_driver failed\n", __FUNCTION__));
goto fail_1;
}
#if (LINUX_VERSION_CODE >=KERNEL_VERSION(2, 6, 27))
/*
* Wait till MMCsdio_register_driver callback called and made driver attach.
* It's needed to make sync up exitfrom dhd insmod and
* Kernel MMC sdio device callbackregistration
*/
if(down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) {
error = -ENODEV;
DHD_ERROR(("%s:sdio_register_driver timeout\n", __FUNCTION__));
goto fail_2;
}
#endif
#if defined(WL_CFG80211)
wl_android_post_init();
#endif
return error;
#if 1 && (LINUX_VERSION_CODE>= KERNEL_VERSION(2, 6, 27))
fail_2:
dhd_bus_unregister();
#endif /* (LINUX_VERSION_CODE >=KERNEL_VERSION(2, 6, 27)) */
fail_1:
#ifdefined(CONFIG_WIFI_CONTROL_FUNC)
wl_android_wifictrl_func_del();
#endif
/* Call customer gpio to turn offpower with WL_REG_ON signal */
dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
return error;
}
2、intwl_android_wifictrl_func_add(void)函数Z:\home\stonechen\svn\TD550_X\TD550\3rdparty\wifi\BCM43362\special\bcmdhd\wl\sys\wl_android.c
intwl_android_wifictrl_func_add(void)
{
int ret = 0;
sema_init(&wifi_control_sem,0);
ret = wifi_add_dev();
if (ret) {
DHD_ERROR(("%s:platform_driver_register failed\n", __FUNCTION__));
return ret;
}
g_wifidev_registered = 1;
/* Waiting callback afterplatform_driver_register is done or exit with error */
if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) {
ret = -EINVAL;
DHD_ERROR(("%s:platform_driver_register timeout\n", __FUNCTION__));
}
return ret;
}
{
DHD_TRACE(("## Callingplatform_driver_register\n"));
platform_driver_register(&wifi_device);
platform_driver_register(&wifi_device_legacy);
return 0;
}
static struct platform_driverwifi_device = {
.probe = wifi_probe,
.remove = wifi_remove,
.suspend = wifi_suspend,
.resume = wifi_resume,
.driver = {
.name = "bcmdhd_wlan",
}
};
static struct platform_driverwifi_device_legacy = {
.probe = wifi_probe,
.remove = wifi_remove,
.suspend = wifi_suspend,
.resume = wifi_resume,
.driver = {
.name = "bcm4329_wlan",
}
};
3、这里我们看到wifi注册平台驱动,那么我们不禁要问wifi平台设备是在哪里注册的??
我们通过find |grep发现该路径是在:/home/stonechen/svn/TD550_TROUT/TD550/customize/customer_cfg/sp8810g-brcm/kernel/wifi/dhd_adapter.c
设备注册函数如下:
static int __initwlan_device_init(void)
{
int ret;
init_wifi_mem();
//wlan_ldo_enable();
gpio_request(sprd_3rdparty_gpio_wifi_irq,"oob_irq"); //irq是在gpio_cfg.c
gpio_direction_input(sprd_3rdparty_gpio_wifi_irq);
wlan_resources[1].start =sprd_alloc_gpio_irq(sprd_3rdparty_gpio_wifi_irq);
//wlan_resources[1].end =gpio_to_irq(sprd_3rdparty_gpio_wifi_irq);
gpio_request(sprd_3rdparty_gpio_wifi_power,"wifi_pwd");
gpio_direction_output(sprd_3rdparty_gpio_wifi_power,0);
ret= platform_device_register(&sprd_wlan_device); //这里注册了平台设备
return ret;
}
late_initcall(wlan_device_init);//这里是首先进入的函数哦
MODULE_DESCRIPTION("Broadcommwlan driver");
MODULE_LICENSE("GPL");
static struct platform_devicesprd_wlan_device = {
.name = "bcmdhd_wlan",//和wifi平台驱动一样的名字,因此匹配执行probe函数
.id = 1,
.dev = {
.platform_data =&wlan_device_control,
},
.resource = wlan_resources,
.num_resources =ARRAY_SIZE(wlan_resources),
};
4、wifi的平台驱动和平台设备都注册之后,将执行platform_probe函数!!
static int wifi_probe(structplatform_device *pdev)
{
struct wifi_platform_data*wifi_ctrl =
(struct wifi_platform_data*)(pdev->dev.platform_data);
此处很重要,获取了device中的很多ops函数!!
DHD_ERROR(("## %s\n",__FUNCTION__));
wifi_irqres =platform_get_resource_byname(pdev, IORESOURCE_IRQ,"bcmdhd_wlan_irq");//此处是获取irq资源,成功!
if (wifi_irqres == NULL)
wifi_irqres =platform_get_resource_byname(pdev,
IORESOURCE_IRQ,"bcm4329_wlan_irq");
wifi_control_data = wifi_ctrl;
wifi_set_power(1, 0); /* Power On*/此处将调用wifi_control_data->set_power(on),也就是获取data中的注册的函数{dhd_adapter.c}intwlan_device_power(int on)
wifi_set_carddetect(1); /*CardDetect (0->1) */同理,也是调用函数dhd_adapter.c中的intwlan_device_set_carddetect(int val)
up(&wifi_control_sem);
return 0;
}
int wlan_device_set_carddetect(intval)
{
pr_info("%s: %d\n",__func__, val);
mdelay(100);
#ifdef CONFIG_WLAN_SDIO
sdhci_bus_scan();//此处将调用\3rdparty\wifi\BCM43362\special\android\kernel\drivers\mmc\host\sprdmci.c中的函数void sdhci_bus_scan(void)
#endif
return 0;
}
总结一下:不难看出probe函数就是执行了2个功能开启wifi电源和扫描sd卡。扫描sd卡的前提需要注册sd驱动,而且sdhci_bus_scan函数调用的一个全局变量也需要赋值!!!
#ifdef SDHCI_BUS_SCAN
void sdhci_bus_scan(void){
if(sdhci_host_g&& (sdhci_host_g->mmc)){ //此处该全局变量是在sdhci_add_host函数中赋值的:sdhci_host_g = host;
printk("%s, entry\n",__func__);
if(sdhci_host_g->ops->set_clock) {
sdhci_host_g->ops->set_clock(sdhci_host_g, 1);
}
sdhci_reset(sdhci_host_g,SDHCI_RESET_ALL);
sdhci_reinit(sdhci_host_g);
mmc_detect_change(sdhci_host_g->mmc,0);
}
return;
}
EXPORT_SYMBOL_GPL(sdhci_bus_scan);
#endif
这里我们就跟踪到了函数sdhci_add_host!!!通过阅读博客文章
http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380146d8d8b492c93cf13d9735b361b31a5a660794613d3b37c6106ac4c56e1f12172415876a09bbe894dd6bd902d3888506e3643d855578e59f9c407739b71cb4de8df0ee0cee733e3fd8485c85523dd54716d8180d1074152&p=826cd017c5934eac5ee6d12d02149c&newp=9f6fc216d9c109ff57ee92755c5195231610db2151ddd21437&user=baidu&fm=sc&query=sdhci_add_host&qid=&p1=1知道了该函数就是SD卡驱动注册的时候调用的。
具体sd卡驱动就不分析了!截取staticint __devinit sdhci_s3c_probe(struct platform_device *pdev)部分代码
host =sdhci_alloc_host(dev, sizeof(struct sdhci_s3c));
。。。。。。。
ret =sdhci_add_host(host);//就是这里调用该函数
if (ret) {
dev_err(dev,"sdhci_add_host() failed\n");
gotoerr_add_host;
}
当然sd卡驱动注册的最后就是调用sdhci_add_host函数。
下面来看sdhci_add_host这个函数。
intsdhci_add_host(structsdhci_host *host)
{
structmmc_host *mmc;
mmc= host->mmc;
tasklet_init(&host->card_tasklet,sdhci_tasklet_card,(unsigned long)host);
tasklet_init(&host->finish_tasklet,sdhci_tasklet_finish,(unsigned long)host);
setup_timer(&host->timer,sdhci_timeout_timer, (unsigned long)host);
request_irq(host->irq,sdhci_irq, IRQF_SHARED,mmc_hostname(mmc), host);//此处注册了中断函数sdhci_irq,很重要
mmc_add_host(mmc);
return0;
}
也许大家很困惑,为什么只关注这几行code,在该函数中主要是集中在mmc_host成员的初始化赋值。由于中断中需要处理的内容较多故采用底半部的办法来处理部分内容。
card_tasklet用于处理MMC插槽上的状态变化。finish_tasklet用于命令传输完成后的处理。Timer用于等待硬件中断。Irq则是SDHCI的中断处理。
将host添加入mmc管理,其本质即是添加host的class_dev
intmmc_add_host(struct mmc_host *host)
{
device_add(&host->class_dev);
mmc_start_host(host);
return0;
}
voidmmc_start_host(struct mmc_host *host) –启动host
{
mmc_power_off(host); --关闭MMC
if(host->caps & MMC_CAP_BOOT_ONTHEFLY)
mmc_rescan(&host->detect.work); --重新扫描
else
mmc_detect_change(host,0); --处理状态改变
}
voidmmc_detect_change(struct mmc_host *host, unsigned long delay)
{
mmc_schedule_delayed_work(&host->detect,delay);
}
在mmc_alloc_host时,存在下面一行code
INIT_DELAYED_WORK(&host->detect,mmc_rescan);
到此就会发现mmc_start_host一定会执行mmc_rescan只不过一个是延时后执行,一个是没有延时执行。
voidmmc_rescan(struct work_struct *work)
{
structmmc_host *host =container_of(work, struct mmc_host, detect.work);
u32ocr;
interr;
mmc_bus_get(host);
if(host->bus_ops == NULL) { --如果为第一次扫描总线上设备。
/*
* Only wecan add a new handler, so it's safe torelease the lock here.
*/
mmc_bus_put(host);
if(host->ops->get_cd && host->ops->get_cd(host) ==0)
goto out;
mmc_claim_host(host); --申请一个host
mmc_power_up(host); --开启host电源
mmc_go_idle(host); --设置host为idle模式
mmc_send_if_cond(host,host->ocr_avail);--是用于验证SD卡接口操作状态的有效性命令(CMD8)。如果SD_SEND_IF_COND指示为符合SD2.0标准的卡,则设置操作状态寄存器ocrbit30指示能够处理块地址SDHC卡
/*
* First wesearch for SDIO...
*/
err =mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
if(mmc_attach_sdio(host, ocr))
mmc_power_off(host);
goto out;
}
/*
* ...thennormal SD...
*/
err =mmc_send_app_op_cond(host, 0, &ocr);
if (!err) {
if(mmc_attach_sd(host, ocr))
mmc_power_off(host);
goto out;
}
/*
* ...andfinally MMC.
*/
err =mmc_send_op_cond(host, 0, &ocr);
if (!err) {
if(mmc_attach_mmc(host, ocr))
mmc_power_off(host);
goto out;
}
mmc_release_host(host);
mmc_power_off(host);
}else {
if(host->bus_ops->detect && !host->bus_dead)
host->bus_ops->detect(host);
mmc_bus_put(host);
}
out:
if(host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect,HZ);
}
那先来看mmc_send_if_cond实现,从字面意思上看该函数就是发送一个SD标准命令,亦如usb的标准命令一样。
在这里不得不先说明一点就是在SD子系统中,所有的数据传输均是由host发起。Host发送完一命令则会等待中断的产生,在这里采用完成量来实现进程的阻塞。
voidmmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
DECLARE_COMPLETION_ONSTACK(complete);
mrq->done_data= &complete;
mmc_start_request(host,mrq); --开始request
wait_for_completion(&complete);
}
mmc_start_request->host->ops->request(即sdhci_request)
static voidsdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
structsdhci_host *host;
unsignedlong flags;
host= mmc_priv(mmc);
spin_lock_irqsave(&host->lock,flags);
host->mrq= mrq;
if((mmc->caps & MMC_CAP_ON_BOARD) || (host->flags &SDHCI_DEVICE_ALIVE))
sdhci_send_command(host,mrq->cmd);
else{
if(!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &SDHCI_CARD_PRESENT)
|| (host->flags& SDHCI_DEVICE_DEAD)) {
host->mrq->cmd->error= -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
} else
sdhci_send_command(host,mrq->cmd);
}
mmiowb();
spin_unlock_irqrestore(&host->lock,flags);
}
sdhci_request->sdhci_send_command发送命令。
当命令传输完成系统调用中断处理函数sdhci_irq。在其中进行中断完成后的处理。
再看注册的中断函数staticirqreturn_t sdhci_irq(int irq, void *dev_id)
{
irqreturn_tresult;
structsdhci_host* host = dev_id;
u32 intmask;
int cardint = 0;
spin_lock(&host->lock);
intmask =sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask ||intmask == 0xffffffff) {
result =IRQ_NONE;
goto out;
}
DBG("*** %sgot interrupt: 0x%08x\n",
mmc_hostname(host->mmc),intmask);
if (intmask &(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
sdhci_writel(host,intmask & (SDHCI_INT_CARD_INSERT |
SDHCI_INT_CARD_REMOVE),SDHCI_INT_STATUS);
tasklet_schedule(&host->card_tasklet);
}
intmask &=~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
if (intmask &SDHCI_INT_CMD_MASK) {
sdhci_writel(host,intmask & SDHCI_INT_CMD_MASK,
SDHCI_INT_STATUS);
sdhci_cmd_irq(host,intmask & SDHCI_INT_CMD_MASK);
}
if (intmask &SDHCI_INT_DATA_MASK) {
sdhci_writel(host,intmask & SDHCI_INT_DATA_MASK,
SDHCI_INT_STATUS);
sdhci_data_irq(host,intmask & SDHCI_INT_DATA_MASK);
}
intmask &=~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
intmask &=~SDHCI_INT_ERROR;
if (intmask &SDHCI_INT_BUS_POWER) {
printk(KERN_ERR"%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
sdhci_writel(host,SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
}
intmask &=~SDHCI_INT_BUS_POWER;
if (intmask &SDHCI_INT_CARD_INT)
cardint = 1;
intmask &=~SDHCI_INT_CARD_INT;
if (intmask) {
printk(KERN_ERR"%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc),intmask);
sdhci_dumpregs(host);
sdhci_writel(host,intmask, SDHCI_INT_STATUS);
}
result =IRQ_HANDLED;
mmiowb();
out:
spin_unlock(&host->lock);
/*
* We have todelay this as it calls back into the driver.
*/
if (cardint)
mmc_signal_sdio_irq(host->mmc);
return result;
}
重新回到函数Z:\home\stonechen\svn\TD550_X\TD550\3rdparty\wifi\BCM43362\special\bcmdhd\dhd\sys\dhd_linux.c
error= dhd_bus_register();
此调用流程由dhd_bus_register发起,通过sdio_register_driver注册一个sdio设备驱动,然后通过dhdsdio_probe初始化并注册一个网络设备,网络设备的注册标志着wifi驱动已经成功加载,关于网络设备的创建,初始化和注册后面会有详细介绍,先来理一下上面的调用流程,:
int
dhd_bus_register(void)
{
DHD_TRACE(("%s:Enter\n", __FUNCTION__));
returnbcmsdh_register(&dhd_sdio);
}
bcmsdh_register(bcmsdh_driver_t*driver)
{
int error = 0;
drvinfo =*driver;
#ifdefined(BCMPLATFORM_BUS)
SDLX_MSG(("LinuxKernel SDIO/MMC Driver\n"));
error =sdio_function_init();
return error;
#endif /*defined(BCMPLATFORM_BUS) */
#if!defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
#if(LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
if (!(error =pci_module_init(&bcmsdh_pci_driver)))
return 0;
#else
if (!(error =pci_register_driver(&bcmsdh_pci_driver)))
return 0;
#endif
SDLX_MSG(("%s:pci_module_init failed 0x%x\n", __FUNCTION__, error));
#endif /*BCMPLATFORM_BUS */
return error;
}
intsdio_function_init(void)
{
int error = 0;
sd_trace(("bcmsdh_sdmmc:%s Enter\n", __FUNCTION__));
gInstance =kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
if (!gInstance)
return -ENOMEM;
error =sdio_register_driver(&bcmsdh_sdmmc_driver);
return error;
}
分析到这里可以看到就是注册了一个sdmmc的driver。然后将执行probe函数
static structsdio_driver bcmsdh_sdmmc_driver = {
.probe =bcmsdh_sdmmc_probe,
.remove =bcmsdh_sdmmc_remove,
.name ="bcmsdh_sdmmc",
.id_table =bcmsdh_sdmmc_ids,
/****Marked byMichael 2012-05-14, to solve WIFI sleep issue caused by SPRD SDIO
#if(LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) &&defined(CONFIG_PM)
******/
.drv = {
.pm =&bcmsdh_sdmmc_pm_ops,
},
/****Marked byMichael 2012-05-14, to solve WIFI sleep issue caused by SPRD SDIO
#endif****Michael*/ /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39))&& defined(CONFIG_PM) */
};
static intbcmsdh_sdmmc_probe(struct sdio_func *func,
conststruct sdio_device_id *id)
{
int ret = 0;
static structsdio_func sdio_func_0;
sd_trace(("bcmsdh_sdmmc:%s Enter\n", __FUNCTION__));
sd_trace(("sdio_bcmsdh:func->class=%x\n", func->class));
sd_trace(("sdio_vendor:0x%04x\n", func->vendor));
sd_trace(("sdio_device:0x%04x\n", func->device));
sd_trace(("Function#:0x%04x\n", func->num));
if (func->num== 1) {
sdio_func_0.num= 0;
sdio_func_0.card= func->card;
gInstance->func[0]= &sdio_func_0;
if(func->device== 0x4) { /* 4318 */
gInstance->func[2]= NULL;
sd_trace(("NICfound, calling bcmsdh_probe...\n"));
ret= bcmsdh_probe(&func->dev);
}
}
gInstance->func[func->num]= func;
if (func->num== 2) {
#ifdefWL_CFG80211
wl_cfg80211_set_parent_dev(&func->dev);
#endif
sd_trace(("F2found, calling bcmsdh_probe...\n"));
ret =bcmsdh_probe(&func->dev);
}
return ret;
}
bcmsdh_probe函数中:
if(!(sdhc->ch = drvinfo.attach((vendevid >> 16),
(vendevid & 0xFFFF), 0, 0, 0, 0,
(void *)regs, NULL, sdh))) {
SDLX_MSG(("%s:device attach failed\n", __FUNCTION__));
gotoerr;
}
此处将执行staticbcmsdh_driver_t dhd_sdio = {
dhdsdio_probe,//执行此函数
dhdsdio_disconnect
};
这是由于之前传递的实参为 return bcmsdh_register(&dhd_sdio);
上面是网络设备注册流程,在dhdsdio_probe函数中先后对dhd_attach和dhd_net_attach两个函数调用,dhd_attach主要用于创建和初始化dhd_info_t和net_device两个结构变量,然后调用dhd_add_if将创建的net_device变量添加到dhd_info_t变量的iflist列表中(支持多接口)。
Dhd_attach的流程如下:
dhd_pub_t*
dhd_attach(osl_t*osh, structdhd_bus *bus, uint bus_hdrlen)
{
dhd_info_t *dhd = NULL;
struct net_device *net = NULL;
......
/* Allocate etherdev, includingspacefor private structure */
if(!(net = alloc_etherdev(sizeof(dhd)))) { //网络设备的创建
DHD_ERROR(("%s: OOM-alloc_etherdev\n", __FUNCTION__));
goto fail;
}
dhd_state|=DHD_ATTACH_STATE_NET_ALLOC;
/* Allocate primary dhd_info */
if(!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) { //dhd的创建
DHD_ERROR(("%s: OOM -allocdhd_info\n", __FUNCTION__));
goto fail;
}
......
/*Set network interface name if it was provided as moduleparameter */
if (iface_name[0]) {
int len;
char ch;
strncpy(net->name,iface_name,IFNAMSIZ);
net->name[IFNAMSIZ - 1] = 0;
len = strlen(net->name);
ch = net->name[len - 1];
if ((ch > '9' || ch <'0') &&(len < IFNAMSIZ - 2))
strcat(net->name,"%d");
}
if(dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0)==DHD_BAD_IF) //将前面创建的net添加到iflist列表中
goto fail;
dhd_state |=DHD_ATTACH_STATE_ADD_IF;
......
Memcpy(netdev_priv(net),&dhd, sizeof(dhd)); //关联dhd和net
//dhd的初始化工作
}
Dhd_add_if的添加网络接口流程:
int
dhd_add_if(dhd_info_t*dhd, int ifidx, void *handle, char *name,
uint8*mac_addr,uint32 flags, uint8 bssidx)
{
dhd_if_t *ifp;
DHD_TRACE(("%s: idx%d,handle->%p\n", __FUNCTION__, ifidx, handle));
ASSERT(dhd && (ifidx<DHD_MAX_IFS));
ifp=dhd->iflist[ifidx];
if (ifp != NULL) {
if (ifp->net != NULL) {
netif_stop_queue(ifp->net);
unregister_netdev(ifp->net);
free_netdev(ifp->net); //如果已经存在,释放net成员
}
} else
if((ifp = MALLOC(dhd->pub.osh,sizeof(dhd_if_t))) == NULL) {
DHD_ERROR(("%s:OOM - dhd_if_t\n", __FUNCTION__)); //否则,创建一个dhd_if_t结构变量
return -ENOMEM;
}
memset(ifp, 0, sizeof(dhd_if_t));
ifp->info= dhd; //进行系列初始化,添加工作
dhd->iflist[ifidx]= ifp;
strncpy(ifp->name,name, IFNAMSIZ);
ifp->name[IFNAMSIZ] = '\0';
if (mac_addr != NULL)
memcpy(&ifp->mac_addr,mac_addr,ETHER_ADDR_LEN);
if (handle == NULL) {
ifp->state = DHD_IF_ADD;
ifp->idx = ifidx;
ifp->bssidx = bssidx;
ASSERT(&dhd->thr_sysioc_ctl.thr_pid>= 0);
up(&dhd->thr_sysioc_ctl.sema);
} else
ifp->net= (struct net_device *)handle; //handle即一个net_device变量
return 0;
}
这样,一个net_device网路设备就被添加到了接口管理列表中了,但是这是网路设备还没有完成初始化和注册工作,bcmsdio_probe函数随后对dhd_net_attach的调用完成了这个操作:
int
dhd_net_attach(dhd_pub_t*dhdp,int ifidx)
{
dhd_info_t *dhd =(dhd_info_t*)dhdp->info;
struct net_device *net = NULL;
int err = 0;
uint8 temp_addr[ETHER_ADDR_LEN] ={0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 };
DHD_TRACE(("%s: ifidx%d\n",__FUNCTION__, ifidx));
ASSERT(dhd &&dhd->iflist[ifidx]);
net =dhd->iflist[ifidx]->net; //首先从刚才添加的接口列表中取出net,然后进行下面的系列初始化工作
ASSERT(net);
//根据内核版本信息,选择对net成员函数的初始化方式,假设是2.6.30的版本
#if(LINUX_VERSION_CODE <KERNEL_VERSION(2, 6, 31))
ASSERT(!net->open);
net->get_stats= dhd_get_stats;
net->do_ioctl=dhd_ioctl_entry;
net->hard_start_xmit= dhd_start_xmit;
net->set_mac_address= dhd_set_mac_address;
net->set_multicast_list= dhd_set_multicast_list;
net->open=net->stop = NULL;
#else
ASSERT(!net->netdev_ops);
net->netdev_ops = &dhd_ops_virt;
#endif
/* Ok, link into the networklayer...*/
if (ifidx == 0) {
/*
* device functions for theprimaryinterface only
*/
#if(LINUX_VERSION_CODE <KERNEL_VERSION(2, 6, 31))
net->open= dhd_open;
net->stop= dhd_stop;
#else
net->netdev_ops= &dhd_ops_pri;
#endif
} else {
/*
* We have to use the primaryMAC forvirtual interfaces
3417,1-8 66%
*/
memcpy(temp_addr,dhd->iflist[ifidx]->mac_addr,ETHER_ADDR_LEN);
/*
* Android sets thelocallyadministered bit to indicate that this is a
*portable hotspot. This will not work in simultaneousAP/STAmode,
* nor with P2P. Need to setthe Donlge's MAC address, andthen use that.
*/
if(!memcmp(temp_addr,dhd->iflist[0]->mac_addr,
ETHER_ADDR_LEN)){
DHD_ERROR(("%sinterface [%s]:set locally administered bit in MAC\n",
__func__,net->name));
temp_addr[0] |= 0x02;
}
}
net->hard_header_len= ETH_HLEN + dhd->pub.hdrlen;
#ifLINUX_VERSION_CODE >=KERNEL_VERSION(2, 6, 24)
net->ethtool_ops= &dhd_ethtool_ops;
#endif/* LINUX_VERSION_CODE>= KERNEL_VERSION(2, 6, 24) */
#ifdefined(CONFIG_WIRELESS_EXT)
#ifWIRELESS_EXT < 19
net->get_wireless_stats= dhd_get_wireless_stats;
#endif/* WIRELESS_EXT < 19*/
#ifWIRELESS_EXT > 12
net->wireless_handlers= (struct iw_handler_def*)&wl_iw_handler_def; //这里的初始化工作很重要,之后的ioctl流程会涉及到对它的使用
#endif/* WIRELESS_EXT > 12*/
#endif/*defined(CONFIG_WIRELESS_EXT) */
dhd->pub.rxsz=DBUS_RX_BUFFER_SIZE_DHD(net);
//设置设备地址
memcpy(net->dev_addr,temp_addr, ETHER_ADDR_LEN);
if((err =register_netdev(net)) != 0) { //注册net
DHD_ERROR(("couldn'tregisterthe net device, err %d\n", err));
goto fail;
}
……
}
到这里net网络设备就被注册到系统中了,设备准备好了就好对设备进行访问了