BCMwifi驅動學習linux
1、代碼路徑:Z:\home\stonechen\svn\TD550_X\TD550\3rdparty\wifi\BCM43362\special\bcmdhd\dhd\sys\dhd_linux.candroid
#if LINUX_VERSION_CODE >=KERNEL_VERSION(2, 6, 0)網絡
late_initcall(#dhd_module_init);app
#elseless
module_init(dhd_module_init);ide
#endifsvn
module_exit(dhd_module_cleanup);函數
以上代碼是wifi驅動的入口函數!!!!post
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網絡設備就被註冊到系統中了,設備準備好了就好對設備進行訪問了