linux設備驅動-wifi驅動詳解2 sdio_wifi驅動詳解


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

對於SDIO接口的wifi,首先,它是一個sdio的卡的設備,然后具備了wifi的功能,所以,注冊的時候還是先以sdio的卡的設備去注冊的。然后檢測到卡之后就要驅動他的wifi功能了,顯然,他是用sdio的協議,通過發命令和數據來控制的。sdio協議是一種單主多從模式。

MMC/SD/SDIO的驅動程序主要分為兩大塊,主設備驅動和從設備驅動。對於wifi來說,CPU上的MMC模塊就是主設備,而WIFI模塊就是從設備。本文主要分析wifi sdio driver的注冊。

1 wifi模塊驅動作為sdio的從設備

wifi模塊驅動的通用的軟件架構

(1)分為兩部分,上面為linux的wifi驅動,下面是wifi chip端的firmware

(2)其中固件部分的主要工作是:因為天線接受和發送回來的都是802.11幀的幀,而主機接受和傳送出來的數據都必須是802.3的幀,所以必須由firmware來負責802.3的幀和802.11幀之間的轉換。所以linux中有線網絡和無線網絡驅動是復用的。

(3)當天線收到數據,並被firmware處理好后會放在一個buffer里,並產生一個中斷,主機在收到中斷后就去讀這個buffer。

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

1 sdio driver的注冊

以linux-4.9.73\drivers\net\wireless\marvell\libertas\If_sdio.c的wifi driver為例

driver module init

 1 static int __init if_sdio_init_module(void)
 2 {
 3     int ret = 0;
 4 
 5     lbs_deb_enter(LBS_DEB_SDIO);
 6 
 7     printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n");
 8     printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n");
 9 
10     ret = sdio_register_driver(&if_sdio_driver);//注冊sdio從設備的driver
11 
12     /* Clear the flag in case user removes the card. */
13     user_rmmod = 0;
14 
15     lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
16 
17     return ret;
18 }

if_sdio_driver的定義

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 };

sdio_register_driver

1 int sdio_register_driver(struct sdio_driver *drv)
2 {
3     drv->drv.name = drv->name;//幫忙driver name 
4     drv->drv.bus = &sdio_bus_type;//綁定總線
5     return driver_register(&drv->drv);//向內核注冊driver
6 }

sdio driver probe函數

  1 static int if_sdio_probe(struct sdio_func *func,
  2         const struct sdio_device_id *id)
  3 {
  4     struct if_sdio_card *card;//定義一個 if_sdio  card的結構體
  5     struct lbs_private *priv;
  6     int ret, i;
  7     unsigned int model;
  8     struct if_sdio_packet *packet;//sdio 包的結構體 
  9 
 10     lbs_deb_enter(LBS_DEB_SDIO);
 11     /*// 查詢是否有指定的功能寄存器在mmc_sdio_card中*/
 12     for (i = 0;i < func->card->num_info;i++) {
 13         if (sscanf(func->card->info[i],
 14                 "802.11 SDIO ID: %x", &model) == 1)
 15             break;
 16         if (sscanf(func->card->info[i],
 17                 "ID: %x", &model) == 1)
 18             break;
 19         if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
 20             model = MODEL_8385;
 21             break;
 22         }
 23     }
 24 
 25     if (i == func->card->num_info) {
 26         pr_err("unable to identify card model\n");
 27         return -ENODEV;
 28     }
 29 
 30     card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL);//分配card
 31     if (!card)
 32         return -ENOMEM;
 33 
 34     card->func = func;
 35     card->model = model;
 36     //在這里進行片選  選擇到使用的marvell 8686 的設備
 37     switch (card->model) {
 38     case MODEL_8385:
 39         card->scratch_reg = IF_SDIO_SCRATCH_OLD;
 40         break;
 41     case MODEL_8686:
 42         card->scratch_reg = IF_SDIO_SCRATCH;
 43         break;
 44     case MODEL_8688:
 45     default: /* for newer chipsets */
 46         card->scratch_reg = IF_SDIO_FW_STATUS;
 47         break;
 48     }
 49 
 50     spin_lock_init(&card->lock);
 51     card->workqueue = alloc_workqueue("libertas_sdio", WQ_MEM_RECLAIM, 0);//分配隊列
 52     INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
 53     init_waitqueue_head(&card->pwron_waitq);
 54 
 55     /* Check if we support this card */
 56     for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
 57         if (card->model == fw_table[i].model)
 58             break;
 59     }
 60     if (i == ARRAY_SIZE(fw_table)) {
 61         pr_err("unknown card model 0x%x\n", card->model);
 62         ret = -ENODEV;
 63         goto free;
 64     }
 65 
 66     sdio_set_drvdata(func, card);
 67 
 68     lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
 69             "device = 0x%X, model = 0x%X, ioport = 0x%X\n",
 70             func->class, func->vendor, func->device,
 71             model, (unsigned)card->ioport);
 72 
 73 
 74     priv = lbs_add_card(card, &func->dev);//添加網絡結構體  分配設備並注冊
 75     if (!priv) {
 76         ret = -ENOMEM;
 77         goto free;
 78     }
 79 
 80     card->priv = priv;
 81 
 82     priv->card = card;
 83     priv->hw_host_to_card = if_sdio_host_to_card;
 84     priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
 85     priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
 86     priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
 87     priv->reset_card = if_sdio_reset_card;
 88     priv->power_save = if_sdio_power_save;
 89     priv->power_restore = if_sdio_power_restore;
 90     priv->is_polling = !(func->card->host->caps & MMC_CAP_SDIO_IRQ);
 91     ret = if_sdio_power_on(card);
 92     if (ret)
 93         goto err_activate_card;
 94 
 95 out:
 96     lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
 97 
 98     return ret;
 99 
100 err_activate_card:
101     flush_workqueue(card->workqueue);
102     lbs_remove_card(priv);
103 free:
104     destroy_workqueue(card->workqueue);
105     while (card->packets) {
106         packet = card->packets;
107         card->packets = card->packets->next;
108         kfree(packet);
109     }
110 
111     kfree(card);
112 
113     goto out;
114 }

函數lbs_add_card

 1 struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
 2 {
 3     struct net_device *dev;
 4     struct wireless_dev *wdev;//創建無線設備結構體
 5     struct lbs_private *priv = NULL;
 6 
 7     lbs_deb_enter(LBS_DEB_MAIN);
 8 
 9     /* Allocate an Ethernet device and register it */
10     wdev = lbs_cfg_alloc(dmdev);//分配無線設備結構體
11     if (IS_ERR(wdev)) {
12         pr_err("cfg80211 init failed\n");
13         goto done;
14     }
15 
16     wdev->iftype = NL80211_IFTYPE_STATION;
17     priv = wdev_priv(wdev);
18     priv->wdev = wdev;
19 
20     if (lbs_init_adapter(priv)) {
21         pr_err("failed to initialize adapter structure\n");
22         goto err_wdev;
23     }
24 
25     dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup);//分配網絡設備
26     if (!dev) {
27         dev_err(dmdev, "no memory for network device instance\n");
28         goto err_adapter;
29     }
30 
31     dev->ieee80211_ptr = wdev;
32     dev->ml_priv = priv;
33     SET_NETDEV_DEV(dev, dmdev);
34     wdev->netdev = dev;//網絡設備結構體賦值給無線設備結構體
35     priv->dev = dev;
36 
37      dev->netdev_ops = &lbs_netdev_ops;//賦值設備操作函數指針結構體
38     dev->watchdog_timeo = 5 * HZ;
39     dev->ethtool_ops = &lbs_ethtool_ops;
40     dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
41 
42     priv->card = card;
43 
44     strcpy(dev->name, "wlan%d");//wifi name
45 
46     lbs_deb_thread("Starting main thread...\n");
47     init_waitqueue_head(&priv->waitq);
48     priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");//創建main函數thread處理RX/TX
49     if (IS_ERR(priv->main_thread)) {
50         lbs_deb_thread("Error creating main thread.\n");
51         goto err_ndev;
52     }
53 
54     priv->work_thread = create_singlethread_workqueue("lbs_worker");
55     INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);
56 
57     priv->wol_criteria = EHS_REMOVE_WAKEUP;
58     priv->wol_gpio = 0xff;
59     priv->wol_gap = 20;
60     priv->ehs_remove_supported = true;
61 
62     goto done;
63 
64  err_ndev:
65     free_netdev(dev);
66 
67  err_adapter:
68     lbs_free_adapter(priv);
69 
70  err_wdev:
71     lbs_cfg_free(priv);
72 
73     priv = NULL;
74 
75 done:
76     lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv);
77     return priv;
78 }

函數lbs_cfg_alloc

 1 struct wireless_dev *lbs_cfg_alloc(struct device *dev)
 2 {
 3     int ret = 0;
 4     struct wireless_dev *wdev;
 5 
 6     lbs_deb_enter(LBS_DEB_CFG80211);
 7 
 8     wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);//分配無線設備結構體
 9     if (!wdev)
10         return ERR_PTR(-ENOMEM);
11 
12     wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private));//注冊網絡設備???
13     if (!wdev->wiphy) {
14         dev_err(dev, "cannot allocate wiphy\n");
15         ret = -ENOMEM;
16         goto err_wiphy_new;
17     }
18 
19     lbs_deb_leave(LBS_DEB_CFG80211);
20     return wdev;
21 
22  err_wiphy_new:
23     kfree(wdev);
24     lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
25     return ERR_PTR(ret);
26 }

2 sdio device

sdio device系統啟動時根據dts來創建,然后根據sdio_bus_match函數匹配上述的sdio driver

3 sdio bus

sdio bus定義

1 static struct bus_type sdio_bus_type = {
2     .name        = "sdio", 3 .dev_groups = sdio_dev_groups, 4 .match = sdio_bus_match, 5 .uevent = sdio_bus_uevent, 6 .probe = sdio_bus_probe, 7 .remove = sdio_bus_remove, 8 .pm = &sdio_bus_pm_ops, 9 };

 

sdio_bus_match函數

 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 
 6     if (sdio_match_device(func, sdrv))
 7         return 1;
 8 
 9     return 0;
10 }
11 
12 static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
13     struct sdio_driver *sdrv)
14 {
15     const struct sdio_device_id *ids;
16 
17     ids = sdrv->id_table;
18 
19     if (ids) {
20         while (ids->class || ids->vendor || ids->device) {
21             if (sdio_match_one(func, ids))//根據device id來匹配
22                 return ids;
23             ids++;
24         }
25     }
26 
27     return NULL;
28 }

4 wifi數據接收

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

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

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

 1 /* Finish power on sequence (after firmware is loaded) */
 2 static void if_sdio_finish_power_on(struct if_sdio_card *card)
 3 {
 4   ...
 5   ret = sdio_claim_irq(func, if_sdio_interrupt);
 6   ...
 7 }
 8 
 9 static void if_sdio_interrupt(struct sdio_func *func)
10 {
11     int ret;
12     struct if_sdio_card *card;
13     u8 cause;
14 
15     lbs_deb_enter(LBS_DEB_SDIO);
16 
17     card = sdio_get_drvdata(func);
18 
19     cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret);//讀取端口上的數據 ,放到card的buffer中
20     if (ret || !cause)
21         goto out;
22 
23     lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause);
24 
25     sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret);
26     if (ret)
27         goto out;
28 
29     /*
30      * Ignore the define name, this really means the card has
31      * successfully received the command.
32      */
33     card->priv->is_activity_detected = 1;
34     if (cause & IF_SDIO_H_INT_DNLD)
35         lbs_host_to_card_done(card->priv);
36 
37 
38     if (cause & IF_SDIO_H_INT_UPLD) {
39         ret = if_sdio_card_to_host(card);//從無線網卡接收到數據 或者說是上報數據
40         if (ret)
41             goto out;
42     }
43 
44     ret = 0;
45 
46 out:
47     lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
48 }

5 wifi數據的發送

 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    //到底結束  發送數據 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM