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    //到底结束  发送数据 

 

posted @ 2020-09-29 22:56  Action_er  阅读(4557)  评论(0编辑  收藏  举报