设为首页 收藏本站
查看: 2127|回复: 0

[经验分享] BCM wifi驱动学习

[复制链接]

尚未签到

发表于 2015-9-30 14:06:31 | 显示全部楼层 |阅读模式
BCMwifi驱动学习
一、wifi详解1
  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;
  }
  


  static int wifi_add_dev(void)
  {
  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
  


二、wifi详解2
  这里我们就跟踪到了函数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;
  mrq->done= mmc_wait_done;
  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;
  }
三、wifi详解3
  重新回到函数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);
四、wifi详解4
  


  上面是网络设备注册流程,在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网络设备就被注册到系统中了,设备准备好了就好对设备进行访问了
  


  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-121015-1-1.html 上篇帖子: Windows Phone获取WiFi BSSID 下篇帖子: c#实现wifi连接器
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表