SDIO接口WiFi驅動淺析

 SDIO-Wifi模塊是基於SDIO接口的符合wifi無線網絡標準的嵌入式模塊,內置無線網絡協議IEEE802.11協議棧以及TCP/IP協議棧,能夠實現用戶主平臺數據通過SDIO口到無線網絡之間的轉換。SDIO具有傳輸數據快,兼容SD、MMC接口等特點。

     對於SDIO接口的wifi,首先,它是一個sdio的卡的設備,然後具備了wifi的功能,所以,註冊的時候還是先以sdio的卡的設備去註冊的。然後檢測到卡之後就要驅動他的wifi功能了,顯然,他是用sdio的協議,通過發命令和數據來控制的。下面先簡單回顧一下SDIO的相關知識:

一、SDIO相關基礎知識解析

1、SDIO接口

       SDIO 故名思義,就是 SD 的 I/O 接口(interface)的意思,不過這樣解釋可能還有點抽像。更具體的說明,SD 本來是記憶卡的標準,但是現在也可以把 SD 拿來插上一些外圍接口使用,這樣的技術便是 SDIO。

       所以 SDIO 本身是一種相當單純的技術,透過 SD 的 I/O 接腳來連接外部外圍,並且透過 SD 上的 I/O 數據接位與這些外圍傳輸數據,而且 SD 協會會員也推出很完整的 SDIO stack 驅動程序,使得 SDIO 外圍(我們稱爲SDIO 卡)的開發與應用變得相當熱門。

       現在已經有非常多的手機或是手持裝置都支持 SDIO 的功能(SD 標準原本就是針對 mobile device 而制定),而且許多 SDIO 外圍也都被開發出來,讓手機外接外圍更加容易,並且開發上更有彈性(不需要內建外圍)。目前常見的 SDIO 外圍(SDIO 卡)有:

· Wi-Fi card(無線網絡卡) 

· CMOS sensor card(照相模塊) 

· GPS card 

· GSM/GPRS modem card 

· Bluetooth card 

        SDIO 的應用將是未來嵌入式系統最重要的接口技術之一,並且也會取代目前 GPIO 式的 SPI 接口。


2、SDIO總線

      SDIO總線 和 USB總線 類似,SDIO也有兩端,其中一端是HOST端,另一端是device端。所有的通信都是由HOST端 發送 命令 開始的,Device端只要能解析命令,就可以相互通信

CLK信號:HOST給DEVICE的 時鐘信號,每個時鐘週期傳輸一個命令。

CMD信號:雙向 的信號,用於傳送 命令 和 反應。

DAT0-DAT3 信號:四條用於傳送的數據線。

VDD信號:電源信號。

VSS1,VSS2:電源地信號。


3、SDIO熱插拔原理

方法:設置一個 定時器檢查 或 插拔中斷檢測

硬件:假如GPG10(EINT18)用於SD卡檢測

GPG10 爲高電平 即沒有插入SD卡

GPG10爲低電平  即插入了SD卡


4、SDIO命令

      SDIO總線上都是HOST端發起請求,然後DEVICE端迴應請求。sdio命令由6個字節組成。

a -- Command:用於開始傳輸的命令,是由HOST端發往DEVICE端的。其中命令是通過CMD信號線傳送的。

b -- Response:迴應是DEVICE返回的HOST的命令,作爲Command的迴應。也是通過CMD線傳送的。

c -- Data:數據是雙向的傳送的。可以設置爲1線模式,也可以設置爲4線模式。數據是通過DAT0-DAT3信號線傳輸的。

      SDIO的每次操作都是由HOST在CMD線上發起一個CMD,對於有的CMD,DEVICE需要返回Response,有的則不需要。

     對於讀命令,首先HOST會向DEVICE發送命令,緊接着DEVICE會返回一個握手信號,此時,當HOST收到迴應的握手信號後,會將數據放在4位的數據線上,在傳送數據的同時會跟隨着CRC校驗碼。當整個讀傳送完畢後,HOST會再次發送一個命令,通知DEVICE操作完畢,DEVICE同時會返回一個響應。

    對於寫命令,首先HOST會向DEVICE發送命令,緊接着DEVICE會返回一個握手信號,此時,當HOST收到迴應的握手信號後,會將數據放在4位的數據線上,在傳送數據的同時會跟隨着CRC校驗碼。當整個寫傳送完畢後,HOST會再次發送一個命令,通知DEVICE操作完畢,DEVICE同時會返回一個響應。


二、SDIO接口驅動

        前面講到,SDIO接口的wifi,首先,它是一個sdio的卡的設備,然後具備了wifi的功能,所以SDIO接口的WiFi驅動就是在wifi驅動外面套上了一個SDIO驅動的外殼,SDIO驅動仍然符合設備驅動的分層與分離思想


     設備驅動層(wifi 設備)

                      |

核心層(向上向下提供接口)

                      |

主機驅動層 (實現SDIO驅動)


        下面先分析SDIO接口驅動的實現,看幾個重要的數據結構(用於核心層與主機驅動層 的數據交換處理)。

[ /include/Linux/mmc/host.h ]

struct mmc_host     用來描述卡控制器

struct mmc_card     用來描述卡

struct mmc_driver  用來描述 mmc 卡驅動

struct sdio_func      用來描述 功能設備

struct mmc_host_ops   用來描述卡控制器操作接口函數功能,用於從 主機控制器層向 core 層註冊操作函數,從而將core 層與具體的主機控制器隔離。也就是說 core 要操作主機控制器,就用這個 ops 當中給的函數指針操作,不能直接調用具體主控制器的函數。

      HOST層驅動分析在 前面的系列文章中 Linux SD卡驅動開發(二) —— SD 卡驅動分析HOST篇 有詳細闡述,下面只簡單回顧一下一些重要函數處理

1、編寫Host層驅動

     這裏參考的是S3C24XX的HOST驅動程序   /drivers/mmc/host/s3cmci.c 

[cpp] view plain copy
  1. static struct platform_driver s3cmci_driver = {  
  2.      .driver  = {  
  3.          .name    = "s3c-sdi",  //名稱和平臺設備定義中的對應  
  4.          .owner   = THIS_MODULE,  
  5.          .pm  = s3cmci_pm_ops,  
  6.      },  
  7.      .id_table = s3cmci_driver_ids,  
  8.      .probe        = s3cmci_probe,  //平臺設備探測接口函數  
  9.      .remove       = __devexit_p(s3cmci_remove),  
  10.      .shutdown = s3cmci_shutdown,  
  11. };  
  12.   
  13. s3cmci_probe(struct platform_device *pdev)  
  14. {  
  15.     //....  
  16.     struct mmc_host *mmc;  
  17.     mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);  //分配mmc_host結構體  
  18.   
  19.     //.....  
  20. }  
  21.   
  22. /*註冊中斷處理函數s3cmci_irq,來處理數據收發過程引起的各種中斷*/  
  23. request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host) //註冊中斷處理函數s3cmci_irq  
  24.   
  25. /*註冊中斷處理s3cmci_irq_cd函數,來處理熱撥插引起的中斷,中斷觸發的形式爲上升沿、下降沿觸發*/  
  26. request_irq(host->irq_cd, s3cmci_irq_cd,IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, DRIVER_NAME, host)  
  27.   
  28. mmc_add_host(mmc);  //initialise host hardware //向MMC core註冊host驅動  
  29. ----> device_add(&host->class_dev); //添加設備到mmc_bus_type總線上的設備鏈表中  
  30. ----> mmc_start_host(host); //啓動mmc host  
  31.   
  32. /*MMC drivers should call this when they detect a card has been inserted or removed.檢測sd卡是否插上或移除*/  
  33.  ---->mmc_detect_change(host, 0);  
  34.   
  35. /*Schedule delayed work in the MMC work queue.調度延時工作隊列*/  
  36.  mmc_schedule_delayed_work(&host->detect, delay);  

搜索host->detected得到以下信息:

[/drivers/mmc/core/host.c]

[cpp] view plain copy
  1. NIT_DELAYED_WORK(&host->detect, mmc_rescan);  
  2.   
  3. mmc_rescan(struct work_struct *work)  
  4. ---->mmc_bus_put(host);//card 從bus上移除時,釋放它佔有的總線空間  
  5.   
  6. /*判斷當前mmc host控制器是否被佔用,當前mmc控制器如果被佔用,那麼  host->claimed = 1;否則爲0 
  7.  *如果爲1,那麼會在while(1)循環中調用schedule切換出自己,當佔用mmc控制器的操作完成之後,執行 *mmc_release_host()的時候,會激活登記到等待隊列&host->wq中的其他 程序獲得mmc主控制器的使用權 
  8.  */  
  9. mmc_claim_host(host);  
  10.      mmc_rescan_try_freq(host, max(freqs[i], host->f_min);  
  11.   
  12. static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)  
  13. {  
  14.      …  
  15.      /* Order's important: probe SDIO, then SD, then MMC */  
  16.      if (!mmc_attach_sdio(host))  
  17.           return 0;  
  18.      if (!mmc_attach_sd(host))  
  19.          return 0;  
  20.      if (!mmc_attach_mmc(host))  
  21.          return 0;  
  22.         ….  
  23. }  
  24.   
  25. mmc_attach_sdio(struct mmc_host *host)  //匹配sdio接口卡  
  26.      --->mmc_attach_bus(host, &mmc_sdio_ops);  
  27.   
  28. /*當card與總線上的驅動匹配,就初始化card*/  
  29. mmc_sdio_init_card(host, host->ocr, NULL, 0);   
  30.     --->card = mmc_alloc_card(host, NULL);//分配一個card結構體  
  31.           mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); //設置mmc_bus的工作模式  
  32.   
  33. struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; //SDIO functions (devices)  
  34.   
  35. sdio_init_func(host->card, i + 1);  
  36.     --->func = sdio_alloc_func(card); //分配struct sdio_fun(sdio功能設備)結構體  
  37.           mmc_io_rw_direct();  
  38.           card->sdio_func[fn - 1] = func;  
  39.   
  40. mmc_add_card(host->card);  //將具體的sdio設備掛載到mmc_bus_types 總線  
  41. sdio_add_func(host->card->sdio_func[i]); //將sdio功能設備掛載到sdio_bus_types總線  

這裏一系列函數調用在前面的SD驅動蚊帳中已經闡述過了,不再詳細闡述


2、SDIO設備的熱插拔

      當插拔SDIO設備,會觸發中斷通知到CPU,然後執行卡檢測中斷處理函數在這個中斷服務函數中,mmc_detect_change->mmc_schedule_delayed_work(&host->detect,delay), INIT_DELAYED_WORK(&host->detect, mmc_rescan)會調度mmc_rescan函數延時調度工作隊列,這樣也會觸發SDIO設備的初始化流程,檢測到有效的SDIO設備後,會將它註冊到系統中去。

[cpp] view plain copy
  1. static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)  
  2. {  
  3.      struct s3cmci_host *host = (struct s3cmci_host *)dev_id;  
  4.      ........  
  5.      mmc_detect_change(host->mmc, msecs_to_jiffies(500));  
  6.   
  7.      return IRQ_HANDLED;  
  8. }  


三、wifi 驅動部分解析

wifi驅動的通用的軟件架構

1. 分爲兩部分,上面爲主機端驅動,下面是我們之前所說的firmware

2. 其中固件部分的主要工作是:因爲天線接受和發送回來的都是802.11幀的幀,而主機接受和傳送出來的數據都必須是802.3的幀,所以必須由firmware來負責802.3的幀和802.11幀之間的轉換

3. 當天線收到數據,並被firmware處理好後會放在一個buffer裏,併產生一箇中斷,主機在收到中斷後就去讀這個buffer。

      

     SDIO設備的驅動由sdio_driver結構體定義,sdio_driver其實是driver的封裝。通過sdio_register_driver函數將SDIO設備驅動加載進內核,其實就是掛載到sdio_bus_type總線上去。

1、設備驅動的註冊與匹配

[Drivers/net/wireless/libertas/if_sdio.c]

[cpp] view plain copy
  1. /* SDIO function device driver*/  
  2.   
  3. struct sdio_driver {  
  4.      char *name;  //設備名  
  5.      const struct sdio_device_id *id_table; //設備驅動ID  
  6.      int (*probe)(struct sdio_func *, const struct sdio_device_id *);//匹配函數  
  7.      void (*remove)(struct sdio_func *);  
  8.      struct device_driver drv;  
  9. };  

下面是具體函數的填充:

[cpp] view plain copy
  1. /*if_sdio.c*/  
  2.   
  3. static struct sdio_driver if_sdio_driver = {  
  4.      .name         = "libertas_sdio",  
  5.      .id_table = if_sdio_ids,  //用於設備與驅動的匹配  
  6.      .probe        = if_sdio_probe,  
  7.      .remove       = if_sdio_remove,  
  8.      .drv = {  
  9.          .pm = &if_sdio_pm_ops,  
  10.          }  
  11. };  

設備註冊函數

[cpp] view plain copy
  1. /** 
  2.  *   sdio_register_driver - register a function driver 
  3.  *   @drv: SDIO function driver 
  4.  */  
  5.   
  6. int sdio_register_driver(struct sdio_driver *drv)  
  7. {  
  8.      drv->drv.name = drv->name;  
  9.      drv->drv.bus = &sdio_bus_type;  //設置driver的bus爲sdio_bus_type  
  10.      return driver_register(&drv->drv);  
  11. }  

總線函數

[cpp] view plain copy
  1. static struct bus_type sdio_bus_type = {  
  2.      .name         = "sdio",  
  3.      .dev_attrs    = sdio_dev_attrs,  
  4.      .match        = sdio_bus_match,  
  5.      .uevent       = sdio_bus_uevent,  
  6.      .probe        = sdio_bus_probe,  
  7.      .remove       = sdio_bus_remove,  
  8.      .pm      = SDIO_PM_OPS_PTR,  
  9. };  

注意:設備或者驅動註冊到系統中的過程中,都會調用相應bus上的匹配函數來進行匹配合適的驅動或者設備,對於sdio設備的匹配是由sdio_bus_matchsdio_bus_probe函數來完成。

[cpp] view plain copy
  1. static int sdio_bus_match(struct device *dev, struct device_driver *drv)  
  2. {  
  3.      struct sdio_func *func = dev_to_sdio_func(dev);  
  4.      struct sdio_driver *sdrv = to_sdio_driver(drv);   
  5.      if (sdio_match_device(func, sdrv))  
  6.          return 1;   
  7.   
  8.      return 0;  
  9. }  
  10.   
  11. static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,  
  12.      struct sdio_driver *sdrv)  
  13. {  
  14.      const struct sdio_device_id *ids;  
  15.      ids = sdrv->id_table;             
  16.   
  17.     if (sdio_match_one(func, ids))  
  18.                    return ids;  
  19. }  

由以上匹配過程來看,通過匹配id_table 和 sdio_driver設備驅動中id,來匹配合適的驅動或設備。最終會調用.probe函數,來完成相關操作。


2、If_sdio_probe函數

    當檢測到sdio卡插入了之後就會調用If_sdio_probe,而當卡被移除後就會調用If_sdio_remove



下面先看下If_sdio_probet函數,if_sdio_prob 函數 主要做了兩件事  

[cpp] view plain copy
  1. static struct sdio_driver if_sdio_driver = {  
  2.  .name  = "libertas_sdio",  
  3.  .id_table = if_sdio_ids,   //用於設備和驅動的匹配  
  4.  .probe  = if_sdio_probe,  
  5.  .remove  = if_sdio_remove,  
  6.  .drv = {  
  7.   .pm = &if_sdio_pm_ops,  
  8.  },  
  9. };  
  10.    
  11.   
  12. //定義一個 if_sdio  card的結構體  
  13.  struct if_sdio_card *card;  
  14.  struct if_sdio_packet *packet;  //sdio 包的結構體   
  15.  struct mmc_host *host = func->card->host;  
  16.   
  17.  // 查詢是否有指定的功能寄存器在mmc  
  18.    //_sdio_card中  
  19.  for (i = 0;i < func->card->num_info;i++) {  
  20.   if (sscanf(func->card->info[i],  
  21.     "802.11 SDIO ID: %x", &model) == 1)  
  22.    
  23. //在這裏進行片選  選擇到我們使用的marvell 8686 的設備  
  24. case MODEL_8686:  
  25.   card->scratch_reg = IF_SDIO_SCRATCH;  
  26.    
  27.    
  28. //創建sdio 的工作隊列   
  29. card->workqueue = create_workqueue("libertas_sdio");  
  30. //調用下面的函數  
  31. INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);  
  32.   
  33.   
  34. //主機到卡的工作隊列  
  35. static void if_sdio_host_to_card_worker(struct work_struct *work)  
  36.   
  37.  /* Check if we support this card  選擇我們所支持的卡的類型*/  
  38.   //賦值爲sd8686_helper.bin   sd8686.bin  
  39. /*fw_table 中的  MODEL_8686, "sd8686_helper.bin""sd8686.bin" },?/  
  40.  for (i = 0; i < ARRAY_SIZE(fw_table); i++) {  
  41.       if (card->model == fw_table[i].model)  
  42.            break;  
  43.  }  
  44.  { MODEL_8688, "libertas/sd8688_helper.bin""libertas/sd8688.bin" },  
  45.    
  46.   
  47. //申請一個host  
  48. sdio_claim_host(func);  
  49. //使能sdio 的功能 寄存器  
  50. ret = sdio_enable_func(func);  
  51. if (ret)  
  52.   goto release;  
  53.   
  54. 2//申請 sdio 的中斷  當有數據  ,命令 或者是事件 的時間執行中斷  
  55. ret = sdio_claim_irq(func, if_sdio_interrupt);  
  56. ret = if_sdio_card_to_host(card);  //從無線網卡接收到數據 或者說是上報數據  
  57. ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);   //接收數據的處理   
  58. ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);   //處理申請的命令中斷  
  59. ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4);//處理申請的事件中斷  
  60.   
  61.   
  62. //添加網絡結構體  分配設備並註冊  
  63. priv = lbs_add_card(card, &func->dev);  
  64.   
  65. //分配Ethernet設備並註冊   
  66.  wdev = lbs_cfg_alloc(dmdev);  
  67. //802無線網的具體的操作函數   
  68. wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private));  
  69.   
  70.    
  71. //分配網絡設備是整個網絡部分操作的  
  72. //的核心結構體  
  73. dev = alloc_netdev(0, "wlan%d", ether_setup);  //實例化wlan0的屬性  
  74. dev->ieee80211_ptr = wdev;  
  75.  dev->ml_priv = priv;  
  76.  //設置設備的物理地址   
  77.  SET_NETDEV_DEV(dev, dmdev);  
  78.  wdev->netdev = dev;  
  79.  priv->dev = dev;  
  80.    //初始化網絡設備 ops.  看門狗    
  81.   dev->netdev_ops = &lbs_netdev_ops;    //網絡設備的具體的操作函數   
  82.  dev->watchdog_timeo = 5 * HZ;  
  83.  dev->ethtool_ops = &lbs_ethtool_ops;     
  84.  dev->flags |= IFF_BROADCAST | IFF_MULTICAST;  //廣播或者多播  
  85.    
  86.    
  87.    
  88.  //啓動一個內核線程來管理這個網絡設備的數據發送,事件的處理(卡的拔出)和一些命令的處理   
  89.  priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");  
  90. //初始化相關的工作隊列  
  91.  priv->work_thread = create_singlethread_workqueue("lbs_worker");  
  92.  INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);  
  93.  priv->wol_criteria = EHS_REMOVE_WAKEUP;  
  94.  priv->wol_gpio = 0xff;  
  95.  priv->wol_gap = 20;  
  96.  priv->ehs_remove_supported = true;  
  97.    
  98.    
  99.  //設置私有變量   
  100. //設置主機發送數據到卡  
  101.  priv->hw_host_to_card = if_sdio_host_to_card;  
  102.  priv->enter_deep_sleep = if_sdio_enter_deep_sleep;  
  103.  priv->exit_deep_sleep = if_sdio_exit_deep_sleep;  
  104.  priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;  
  105.  sdio_claim_host(func);    
  106.   
  107.   //啓動卡設備   
  108.  ret = lbs_start_card(priv);  
  109.  if (lbs_cfg_register(priv))   
  110.   
  111.  ret = register_netdev(priv->dev);  
  112.  err = register_netdevice(dev);  
  113.   
  114.    
  115. //具體的wifi設備驅動功能   
  116. //網絡設備操作的具體函數   
  117. static const struct net_device_ops lbs_netdev_ops = {  
  118.  .ndo_open   = lbs_dev_open,   //打開  
  119.  .ndo_stop  = lbs_eth_stop,  //停止  
  120.  .ndo_start_xmit  = lbs_hard_start_xmit,   //開始發送數據  
  121.  .ndo_set_mac_address = lbs_set_mac_address,   //設置mac地址   
  122.  .ndo_tx_timeout  = lbs_tx_timeout,    //發送超時  
  123.  .ndo_set_multicast_list = lbs_set_multicast_list,   //多播地址  
  124.  .ndo_change_mtu  = eth_change_mtu,  //最大傳輸單元  
  125.  .ndo_validate_addr = eth_validate_addr,  //判斷地址的有效性   

3、數據的接收,通過中斷的方式來解決

     網絡設備接收數據的主要方法是由中斷引發設備的中斷處理函數,中斷處理函數判斷中斷的類型,如果爲接收中斷,則讀取接收到的數據,分配sk_buff數據結構和數據緩衝區,並將接收的數據複製到數據緩存區,並調用netif_rx()函數將sk_buff傳遞給上層協議。

    搜索if_sdio_interrupt,可知道它是在if_sdio.c文件中if_sdio_probe()函數中sdio_claim_irq(func, if_sdio_interrupt) ,func->irq_handler = if_sdio_interrupt。當s3cmci_irq中斷處理函數的S3C2410_SDIIMSK_SDIOIRQ 中斷被觸發時將調用if_sdio_interrupt()函數,進行接收數據。

[cpp] view plain copy
  1. static void if_sdio_interrupt(struct sdio_func *func)  
  2.   
  3. ret = if_sdio_card_to_host(card);  //從無線網卡接收到數據 或者說是上報數據  
  4. //讀取端口上的數據 ,放到card的buffer中   
  5.  ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);  
  6. 1.在這裏一方面處理中斷  還有2   
  7.  switch (type) {   //處理cmd   data  event的請求   
  8.  case MVMS_CMD:  
  9.   ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);   //處理申請的命令中斷  
  10.   if (ret)  
  11.    goto out;  
  12.   break;  
  13.  case MVMS_DAT:  
  14.   ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);//處理申請的數據中斷   
  15.   if (ret)  
  16.    goto out;  
  17.   break;  
  18.  case MVMS_EVENT:  
  19.   ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4);//處理申請的事件中斷  
  20.    
  21. //讀取包的過程   
  22.  lbs_process_rxed_packet(card->priv, skb);  
  23.    
  24.  //如果是中斷 ,就把skb這個包提交給協議層,這個函數是  
  25.  //協議層提供的  netif_rx(skb)  
  26.  if (in_interrupt())  
  27.   netif_rx(skb);    //提交給協議層   
  28.    
  29.    
  30. 2//讀取端口上的數據 ,放到card的buffer中   
  31.  ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);  
  32. //讀取地址,目的地址,數量 等  
  33. int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, int count)  
  34.   
  35.          return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);  
  36.   
  37.                 ret = mmc_io_rw_extended(func->card, write,func->num, addr, incr_addr, buf,blocks, func->cur_blksize);  
  38.                          cmd.arg = write ? 0x80000000 : 0x00000000;  
  39.                                   
  40.                     //wait for  request    
  41.                      mmc_wait_for_req(card->host, &mrq);  
  42.                         開始應答   
  43.                          mmc_start_request(host, mrq);  
  44.                          wait_for_completion(&complete);  
  45.                                       
  46.                              host->ops->request(host, mrq);  

4、數據發送

[cpp] view plain copy
  1. //IP層通過dev_queue_xmit()將數據交給網絡設備協議接口層,網絡接口層通過netdevice中的註冊函數的數據發送函數  
  2. int dev_queue_xmit(struct sk_buff *skb)  
  3.   
  4.     if (!netif_tx_queue_stopped(txq)) {  
  5.     __this_cpu_inc(xmit_recursion);  
  6.    //設備硬件開始發送    
  7.     rc = dev_hard_start_xmit(skb, dev, txq);  
  8.   //調用wifi網絡中的ops   
  9.   
  10.   rc = ops->ndo_start_xmit(skb, dev);  
  11.   
  12.   dev->netdev_ops = &lbs_netdev_ops;    //設備的操作函數   
  13.   
  14.  //處理sdio firware數據和內核的數據main_thread 主線程    
  15.  priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");  
  16.   
  17.    //調用host_to_card   即if_sdio_card_to_host函數。   
  18.    int ret = priv->hw_host_to_card(priv, MVMS_DAT,priv->tx_pending_buf,priv->tx_pending_len);  
  19. 爲什麼是if_sdio_to_host呢 ?因爲在prob函數中定義了這一個  
  20. //設置主機發送數據到卡  
  21.  priv->hw_host_to_card = if_sdio_host_to_card;  
  22.      
  23. static int if_sdio_host_to_card(struct lbs_private *priv,u8 type, u8 *buf, u16 nb)  
  24.       //把buf中的數據 copy到sdio 包中,在對sdio 的包進行處理  
  25.          memcpy(packet->buffer + 4, buf, nb);  
  26. //創建工作隊列    
  27.          queue_work(card->workqueue, &card->packet_worker);  
  28.  //初始化隊列    
  29.  INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);  
  30.   
  31.  //sdio的寫數據     
  32.    ret = sdio_writesb(card->func, card->ioport, packet->buffer, packet->nb);  
  33.          //mmc寫擴展口   
  34.                ret = mmc_io_rw_extended(func->card, write,func->num, addr, incr_addr, buf,blocks, func->cur_blksize);  
  35.   
  36.                     //wait for  request    
  37.                                  mmc_wait_for_req(card->host, &mrq);  
  38.                                     
  39.                              mrq->done_data = &complete;  
  40.                              mrq->done = mmc_wait_done;  
  41.                              mmc_start_request(host, mrq);  
  42.                                 //完成等待 寫數據結束   
  43.                              wait_for_completion(&complete);  
  44.    
  45.    
  46.                              host->ops->request(host, mrq);  
  47.    //到底結束  發送數據    


5、移除函數

       當sdio卡拔除時,驅動會調用該函數,完成相應操作。如釋放佔有的資源,禁止func功能函數,釋放host。

[cpp] view plain copy
  1. if_sdio_remove(struct sdio_func *func)  
  2. ---->lbs_stop_card(card->priv);  
  3.      lbs_remove_card(card->priv);  
  4.      ---->kthread_stop(priv->main_thread);  //終止內核線程  
  5.   
  6.          lbs_free_adapter(priv);  
  7.          lbs_cfg_free(priv);  
  8.           free_netdev(dev);  
  9.   
  10.      flush_workqueue(card->workqueue);  //刷新工作隊列  
  11.      destroy_workqueue(card->workqueue);  
  12.      sdio_claim_host(func);  
  13.      sdio_release_irq(func);  
  14.      sdio_disable_func(func);  
  15.       sdio_release_host(func);  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章