现在的位置: 首页 > 综合 > 正文

android_wifi读书笔记之8-SDIO驱动架构

2018年08月23日 ⁄ 综合 ⁄ 共 15782字 ⁄ 字号 评论关闭
文章目录

本文为读书笔记,整理自网络文献和源码

8、SDIO驱动架构

8.1  SDIO协议

SDIO协议是由SD卡的协议演化升级而来的,很多地方保留了SD卡的读写协议,同时SDIO协议又在SD卡协议之上添加了CMD52和CMD53命令。由于这个,SDIO和SD卡规范间的一个重要区别是增加了低速标准,低速卡的目标应用是以最小的硬件开始来支持低速I/O能力

8.1.1 SDIO总线

       SDIO总线和USB总线类似,SDIO总线也有两端,其中一端是主机(HOST)端,另一端是设备端(DEVICE),采用HOST- DEVICE这样的设计是为了简化DEVICE的设计,所有的通信都是由HOST端发出命令开始的。在DEVICE端只要能解溪HOST的命令,就可以同HOST进行通信了。

       SDIO的HOST可以连接多个DEVICE。几种信号如下:

1.       CLK信号:HOST给DEVICE的时钟信号.

2.       CMD信号:双向的信号,用于传送命令和反应。

3.       DAT0-DAT3 信号:四条用于传送的数据线。

4.       VDD信号:电源信号。

5.       VSS1,VSS2:电源地信号。

在SDIO总线定义中,DAT1信号线复用为中断线。在SDIO的1BIT模式下DAT0用来传输数据,DAT1用作中断线。在SDIO的4BIT模式下DAT0-DAT3用来传输数据,其中DAT1复用作中断线。

8.1.2 sdio命令

SDIO总线上都是HOST端发起请求,然后DEVICE端回应请求。sdio命令由6个字节组成。

1. Command:用于开始传输的命令,是由HOST端发往DEVICE端的。其中命令是通过CMD信号线传送的。

2.Response:回应是DEVICE返回的HOST的命令,作为Command的回应。也是通过CMD线传送的。

3. Data:数据是双向的传送的。可以设置为1线模式,也可以设置为4线模式。数据是通过DAT0-DAT3信号线传输的。

  SDIO的每次操作都是由HOST在CMD线上发起一个CMD,对于有的CMD,DEVICE需要返回Response,有的则不需要。

sd命令格式

      以IO_SEND_OP_COND命令为例包含以下部分:

     S(开始位) 总为0

     D(方向位) 1 从host到 device (0  从device到host)

     命令索引:  通过值000101B来

     填充位    0

     IO_OCR     运转条件寄存器所支持的VDD的最小值和最大值

     CRC7       7位CRC校验数据

     E(结束位)  总为1

 

MMC命令总共有40多个,分为class0 ~class7共8类,class0的所有卡必须支持。驱动程序通过发送cmd1、cmd41来区分sd卡和mmc卡,如果发送cmd1返回成功,则为mmc卡,否则发送cmd41返回成功,则为sd卡。 

SDIO 命令清单   

SD Memorycmd   SDIO cmd

  CMD0        CMD52 CMD0是复位命令,为了复位SDIO,用CMD52写1到RES位     CMD12        CMD52 停止块数据传输CMD,写CCCR寄存器中的相应位     

CMD16       CMD52 设置块长度,对SD内存,用CMD5写块长度到FBR寄存器     CMD2         NONE  CID寄存器不存在SDIO CARD中

CMD4         NONE DSR寄存器不存在SDIO CARD中    

CMD9         NONE CSD寄存器不存在SDIO CARD中    

CMD10        NONE CID寄存器不存在SDIO CARD中    

CMD13        NONE  SDIO不支持

ACMD6        CMD52 设置总线宽度,通过些CCCR寄存器     

ACMD13       NONE  SDIO中不支持

ACMD41      CMD5 SDIO 卡和 HOST  用 IO_SEND_OP_COND_COMMAND(CMD5)      ACMD42      CMD52 在SD模式,上拉电阻在 DAT[3]被控制通过写CD Disable      ACMD51      NONE   不支持     

CMD17       CMD53 IO 块操作用CMD53,而不是内存块操作如果是IO功能的卡被激活,仅些CCCR被要求改变的位模式就可以了。如果仅内存激活,通过ACMD6改变总线宽度。如果两者都有,则激活两者需要两个命令。

 

 

8.1.3 SDIO的寄存器:

      SDIO卡的设备驱动80%的任务就是操作SDIO卡上的有关寄存器。SDIO卡最多允许有7个功能(function),这个同其功能号是对应的(0~7),每个功能都对应一个128K字节大小的寄存器, 这些寄存器的详细分区已经其对应的功能。功能号之所以取值范围是1~7,而没有包含0,是因为功能0并不代表真正的功能,而代表CIA寄存器,即CommonI/O Area,这个纪录着SDIO卡的一些基本信息和特性,并且可以改写这些寄存器。其中地址0x1000~0x17fff是SDIO卡的CIS区域,就是基本信息区域,Common
Information Structure。初始化的时候读取并配对SDIO设备。

 

8.2 SDIO驱动结构

Linux mmc子系统驱动架构如下:

 

drivers/mmc下有三个目录的作用分别为:

1) card   这个目录是衔接最上层应用的接口,应用层使用sd卡一般都是通过文件系统来操作的,card目录里面的代码就是让sd卡成为一个块设备,这样应用层就可以把它当作磁盘来操作了。

2) core   这个目录是MMC子系统的核心,里面实现了card和host要用到的一些通用的函数接口和数据结构,它起到衔接作用,可以看作成中间层,是MMC总线驱动程序。mmc/sd/sdio协议部分就是在这个文件夹里面实现的(card目录里面也会涉及到协议)。

 3) host  这个目录存放了各个mmc/sd/sdio控制器的代码,最终操作mmc/sd/sdio卡的部分      (硬件通讯接口)就是在这里实现的。

 

几个重要的数据结构:该结果位于core核心层,主要用于核心层与主机驱动层的数据交换处理。/include/linux/mmc/host.h

struct mmc_host用来描述卡控制器

structmmc_card 用来描述卡

structmmc_driver 用来描述mmc卡驱动

structsdio_func  用来描述功能设备

structmmc_host_ops 用来描述卡控制器操作接口函数功能,用于从主机控制器层向core 层注册操作函数,从而将core 层与具体的主机控制器隔离。也就是说 core 要操作主机控制器,就用这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数。

8.2.1核心部分

核心部分肯定是在core目录里。文件core.c里mmc_init负责初始化mmc子系统subsys_initcall(mmc_init); 主要代码:

         ret= mmc_register_bus();

         if(ret)

                   gotodestroy_workqueue;

 

         ret= mmc_register_host_class();

         if(ret)

                   gotounregister_bus;

 

         ret= sdio_register_bus();

         if(ret)

                   gotounregister_host_class;

主要工作是:

1、 mmc_register_bus  注册mmc总线,其实现:

int mmc_register_bus(void)

{

         returnbus_register(&mmc_bus_type);

}

这个总线主要是为card目录里实现的mmc设备驱动层和mmc控制器实例化一个mmc(包括sd/sdio)设备对象建立的。

static struct bus_type mmc_bus_type = {

.name                = "mmc",

.dev_attrs        = mmc_dev_attrs,

.match               = mmc_bus_match,

.uevent              = mmc_bus_uevent,

.probe                = mmc_bus_probe,

.remove            = mmc_bus_remove,

.suspend  = mmc_bus_suspend,

.resume            = mmc_bus_resume,

};

 

int mmc_register_bus(void)

{

returnbus_register(&mmc_bus_type);

}

这个数组mmc_bus_type里,包含了很多函数了,都是处理总线mmc_bus的.这里,我们主要关注mmc_bus_match与mmc_bus_probe,此两函数将要在device_register和driver_register向总线注册设备的时候被调用。

static int mmc_bus_match(structdevice *dev, struct device_driver *drv)

{

         return1;

}

mmc_bus_match总是返回1,表示成功,由driver的probe方法中确定是否匹配。

static int mmc_bus_probe(struct device *dev)

{

structmmc_driver *drv = to_mmc_driver(dev->driver);

structmmc_card *card = dev_to_mmc_card(dev);

 

return drv->probe(card);

}

在使用bus_register之后,我们可以在sysfs的/sys/bus目录里看到它(mmc)。

2、 mmc_register_host_class,注册mmc控制器类,该方法在host.c中实现。

static struct class mmc_host_class= {

.name                = "mmc_host",

.dev_release    = mmc_host_classdev_release,

};

int mmc_register_host_class(void)

{

         returnclass_register(&mmc_host_class);

}

mmc_register_host_classclass_register之后,在/sys/class目录下将出现mmc_host目录

 

3、 sdio_register_bus这是注册sdio总线,其实现在sdio_bus.c中:

sdio_config_attr(class,"0x%02x\n");

sdio_config_attr(vendor,"0x%04x\n");

sdio_config_attr(device,"0x%04x\n");

static struct device_attributesdio_dev_attrs[] = {

__ATTR_RO(class),

__ATTR_RO(vendor),

__ATTR_RO(device),

__ATTR_RO(modalias),

__ATTR_NULL,

};

staticstruct bus_type sdio_bus_type = {

         .name                ="sdio",

         .dev_attrs        = sdio_dev_attrs,

         .match               =sdio_bus_match,

         .uevent              =sdio_bus_uevent,

         .probe                =sdio_bus_probe,

         .remove            =sdio_bus_remove,

};

 

intsdio_register_bus(void)

{

         returnbus_register(&sdio_bus_type);

}

在使用bus_register之后,我们可以在sysfs的/sys/bus目录里看到它(sdio)。

 

8.2.2控制器host驱动部分

     这部分是在host目录里。以三星的文件sdhci-s3c.c和sdhci.c.

驱动注册:

staticstruct platform_driver sdhci_s3c_driver = {

         .probe                =sdhci_s3c_probe,

         .remove            =__devexit_p(sdhci_s3c_remove),

         .suspend  = sdhci_s3c_suspend,

         .resume           = sdhci_s3c_resume,

         .driver                ={

                   .owner     = THIS_MODULE,

                   .name       = "s3c-sdhci",

         },

};

 

staticint __init sdhci_s3c_init(void)

{

         returnplatform_driver_register(&sdhci_s3c_driver);

}

 

类似于i2c、spi这些控制器,都是以平台设备的方式实现控制器的驱动。

 

sdhci_s3c_probe主要完成的工作是:

1)  irq = platform_get_irq(pdev, 0);

         platform_get_resource(pdev,IORESOURCE_MEM, 0);

         获取平台资源

     

2)  host= sdhci_alloc_host(dev, sizeof(structsdhci_s3c));

     这个基本上是所有相类似驱动的必备过程,实例化一个控制器对象,不过一般都会额外需要自己定义一些数据来维护整个驱动,可以说是上下文数据。调用的方法其实是mmc = mmc_alloc_host(sizeof(structsdhci_host) + priv_size, dev); 这个函数的第一个参数sizeof(struct sdhci_host) + priv_size就是分配私有的数据。mmc_alloc_host这个接口就是core目录里面的host.c提供的。在控制器驱动这边看来,mmc_alloc_host就是核心层为它隐藏了很多细节。

 

3)初始化自己私有部分数据,在probe下准备定调用sdhci_add_host函数(在sdhci.c中)配置io、提取平台设备的信息注册中断、建立寄存器资源映射、分配dma(如果有使能)、请求检测管脚的gpio及注册中断(如果有使能detect)、请求写保护管脚gpio(如果有使能写保护)、开启控制器时钟、配置host参数,MMC core注册host驱动mmc_add_host(mmc),并初始化了两个tasklet
(分别是sdhci_tasklet_card、sdhci_tasklet_finish)。

mmc_add_host(mmc)中:

device_add(&host->class_dev); //添加设备到mmc_bus_type总线上的设备链表中

mmc_start_host(host); //启动mmc host。

sdhci_ops结构体:

staticstruct  mmc_host_ops  sdhci_ops = {

         .request   = sdhci_request,

         .set_ios    = sdhci_set_ios,

         .get_ro              =sdhci_get_ro,

         .enable_sdio_irq =sdhci_enable_sdio_irq,

};

if(host->ops->get_ro)

         sdhci_ops.get_ro =host->ops->get_ro;

mmc->ops= &sdhci_ops;

……

sdhci_s3c _get_ro: 这个函数通过从 GPIO读取,来判断我们的卡是否是写保护的。

sdhci_s3c _set_ios依据核心层传递过来的 ios,来设置硬件 IO,包括引脚配置,使能时钟,和配置总线带宽。

sdhci_s3c_request 这个 函数是最主要,也最复杂的函数,实现了命令和数据的发送和接收。

sdhci_enable_sdio_irq:和sdio中断相关

这些函数是核心层回调用到的,所以需要初始化它们,sdhci_ops是控制器操作集,编写控制器驱动的一个主要任务就是实现这个操作集。下面简单分析下每个函数, mmc驱动绝大部分工作就是写mmc控制器的驱动sdhci_ops提供的操作集实现了4个函数。

sdhci_request最终执行硬件操作流程的函数,是重要的函数。可以核心层通过回调sdhci_request函数发送数据或者命令。函数原型:

static void sdhci_request(struct mmc_host*mmc, struct mmc_request *mrq)

mmc和mrq这两个参数都是核心层定义的数据类型,第一个是在probe的时候我们通过mmc_alloc_host分配的,里面还有我们自己的私有数据!第二个是mmc_request类型:

structmmc_request {

         struct mmc_command    *sbc;                  /*SET_BLOCK_COUNT for multiblock */

         struct mmc_command    *cmd;

         struct mmc_data              *data;

         struct mmc_command    *stop;

         void                    *done_data;    /* completion data */

         void                    (*done)(structmmc_request *);/* completion function */

};

结构体描述(struct mmc_command)和数据有数据的结构体描述(struct mmc_data),同时还有回调函数。

staticvoid sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)

{

         struct sdhci_host *host;

         bool present;

         unsigned long flags;

 

         host = mmc_priv(mmc);

 

         spin_lock_irqsave(&host->lock,flags);

 

         WARN_ON(host->mrq != NULL);

 

#ifndefSDHCI_USE_LEDS_CLASS

         sdhci_activate_led(host);

#endif

         host->mrq = mrq;

         /* If polling, assume that the card isalways present. */

         if ((host->quirks &SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||

            (host->quirks & SDHCI_QUIRK_BROKEN_CARD_PRESENT_BIT))

                   present = true;

         else

                   present = sdhci_readl(host,SDHCI_PRESENT_STATE) &

                                     SDHCI_CARD_PRESENT;

 

         if (!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_send_command函数中:

sdhci_prepare_data(host, cmd->data);

sdhci_writel(host,cmd->arg, SDHCI_ARGUMENT);

sdhci_set_transfer_mode(host,cmd->data);

 

s3cmci_send_command工作包括传输模式的设置(sdhci_set_transfer_mode()函数是用来设置HOST的传输模式),同时还包括数据操作的准备工作,处理函数是sdhci_prepare_data(),函数中实现了函数超时设置和DMA寄存器的相关配置,以及HOST CONTROL 寄存器的配置,最后将命令发送出去。

当命令传输完成系统调用中断处理函数sdhci_irq。在其中进行中断完成后的处理。

8.2.3 mmc驱动层(这部分没有核对和整理)

Mmc层驱动实际上就实现了mmc/sd/sdio/三类设备的驱动。它具体的实现都在card目录下。下面简单分析下:

       block.c中的mmc_blk_init负责初始化mmc层。

主要工作:

因为是向上层展示为块设备,所以第一步就是res =register_blkdev(MMC_BLOCK_MAJOR, "mmc");申请了块设备号MMC_BLOCK_MAJOR(179), 然后就将自己注册到mmc总线上去了,这个总线的初始化是在核心层的初始化中,上文有讲到。

res = mmc_register_driver(&mmc_driver);

这会导致与mmc总线上由控制器注册的设备匹配(至于控制器是怎么添加的后文在讲解),于是mmc_blk_probe函数会调用它的实现很简单:

1)分配一个上下文数据struct mmc_blk_data, 实际上该对象内嵌了块设备对象,同时也初始化了块队列处理函数和块操作集

2)add_disk将struct mmc_blk_data内部已经初始化好的块设备对象添加的块子系统中.

mmc子系统的一些细节实现

 

1、卡(mmc/sd/sdio)的热插拔处理

       卡的热插拔的第一步初始化是在mmc_alloc_host中,也就是由核心层负责这事:

mmc_alloc_host

{

       ….

       INIT_DELAYED_WORK(&host->detect,mmc_rescan);

       …..

}

mmc_rescan负责扫描设备并添加设备,但是因为采用的中断方式触发扫描,所以同时还需要控制器的帮助,这就涉及到probe里面的:

if(!host->pdata->no_detect) {

              ret =gpio_request(host->pdata->gpio_detect, "s3cmci detect");

              if (ret) {

                     dev_err(&pdev->dev,"failed to get detect gpio\n");

                     goto probe_free_irq;

              }

 

              host->irq_cd =gpio_to_irq(host->pdata->gpio_detect);

 

              if (host->irq_cd >= 0) {

                     if(request_irq(host->irq_cd, s3cmci_irq_cd,

                                   IRQF_TRIGGER_RISING |

                                  IRQF_TRIGGER_FALLING,

                                   DRIVER_NAME,host)) {

                           dev_err(&pdev->dev,

                                   "can'tget card detect irq.\n");

                            ret = -ENOENT;

                            gotoprobe_free_gpio_cd;

                     }

              } else {

                    dev_warn(&pdev->dev,

                             "host detecthas no irq available\n");

                    gpio_direction_input(host->pdata->gpio_detect);

              }

       }

}

控制器从控制器平台设备端拿到监控相关的数据,比如哪个管脚。然后控制器为该管脚注册中断。

看一下s3cmci_irq_cd:

staticirqreturn_t s3cmci_irq_cd(int irq, void *dev_id)

{

       struct s3cmci_host *host = (structs3cmci_host *)dev_id;

 

       dbg(host, dbg_irq, "carddetect\n");

 

       mmc_detect_change(host->mmc,msecs_to_jiffies(500));

 

       return IRQ_HANDLED;

}

由此我们可以知道控制器监测管脚的中断处理很简单,只需要调用核心层指定的函数mmc_detect_change即可。至于内部的细节就不分析了,多说两句:里面通过工作队列异步触发

工作者线程kmmcd(由核心层分配的),至于是什么工作呢这个就是上面说的mmc_rescan。上面说的就是热插拔的第一步,事件的触发监控。

第二步就是具体的热插拔处理流程了。

mmc_rescan的主要工作是(注意:这些都是核心层完成的,控制器驱动不用关心,但需要实现核心层要求的操作集,比如s3cmci_ops):

1)判断当前控制器上的卡是否之前已经存在,这种情况下只需要调用操作集里的监测函数:

if((host->bus_ops != NULL) && host->bus_ops->detect &&!host->bus_dead)

             host->bus_ops->detect(host);

这里的总线操作集bus_ops是在添加卡的时候赋值的,可以为

mmc_sdio_ops

mmc_sd_ops(mmc_sd_ops_unsafe)

mmc_ops

对于sd卡,就是mmc_sd_ops

staticconst struct mmc_bus_ops mmc_sd_ops = {

       .remove = mmc_sd_remove,

       .detect = mmc_sd_detect,

       .suspend = NULL,

       .resume = NULL,

       .power_restore = mmc_sd_power_restore,

};

这里面的实现就牵涉到具体的协议了,这里就不分析了。

 

2)如果确实是卡存在,则退出,如果不是,则调用控制器注册的函数

在s3cmci_ops里就是s3cmci_card_present,这个函数简单,就是读gpio管脚电平,不多分析了。然后进入到扫描最核心的部分:

       mmc_claim_host(host);//锁定控制器

 

       mmc_power_up(host);//控制器上电

       sdio_reset(host);

       mmc_go_idle(host); //进入空闲状态,这个是规范里面有的,怎么进入规范里面也有说明

 

       mmc_send_if_cond(host,host->ocr_avail); //发送控制器兼容的电压

 

       /*

        * First we search for SDIO...

        *///判断是否为sdio接口的设备

       err = mmc_send_io_op_cond(host, 0,&ocr);

       if (!err) {

              if (mmc_attach_sdio(host, ocr))

                     mmc_power_off(host);

              goto out;

       }

 

       /*

        * ...then normal SD...

        *///判断是否为sd卡

       err = mmc_send_app_op_cond(host, 0,&ocr);

       if (!err) {

              if (mmc_attach_sd(host, ocr))

                     mmc_power_off(host);

              goto out;

       }

 

       /*

        * ...and finally MMC.

        *///判断是否为mmc卡

       err = mmc_send_op_cond(host, 0,&ocr);

       if (!err) {

              if (mmc_attach_mmc(host, ocr))

                     mmc_power_off(host);

              goto out;

       }

 

还是以sd卡为例:

mmc_send_app_op_cond主要工作是:

1)发送命令41(该命令是sd支持的,但mmc卡不支持的命令

2)如果有正确响应,就代表是sd卡了,进入到mmc_attach_sd

 

mmc_attach_sd的主要实现:

       设置sd卡的总线操作集:mmc_sd_attach_bus_ops(host);

mmc_sd_init_card分配并初始化一个sd卡对象

mmc_add_card将sd卡对象添加的mmc总线

这次的添加动作可能会引起card目录里面注册的mmc_driver的匹配,最终会在mmc_blk_probe实现向应用层提供块设备。

 

2、应用层读、写、控制卡流程(注意:这些都是核心层完成的,控制器驱动不用关心,但需要实现核心层要求的操作集,比如s3cmci_ops):

 

应用层一般都是操作基于sd卡块设备上的文件系统,所以会先经过vfs—>具体的文件系统的read、write-à块子系统submit_bio-à块请求队列处理函数即在card目录里面

       mmc_blk_probe—>mmc_blk_allocàmmc_init_queueàmmc_request函数

该函数会唤醒队列处理线程mmcqd,mmcqd结合mmc_blk_issue_rq将上层 的struct request_queue *q转为协议相关的请求,最终调用到控制器注册的 函数集里面的

s3cmci_request

 

现在我们看控制器怎么实现请求的处理的:

1)调用s3cmci_send_request

它会判断是否附带有数据的传输,如果是,那先对数据处理一下(比如如果是pio传输,那么采用pio方式将数据放到控制器fifo里去,如果是dma传输,则申请dma资源并配置好地

 

2)s3cmci_send_command触发命令的发送,这部分可以参考前文中的描述。

8.3  SDIO卡驱动

8.3.1、热插拔sdio设备检测识别设备流程:

中断或者外部事件来了之后,执行中断的tasklet:

sdhci_tasklet_card

àmmc_detect_change(host->mmc, msecs_to_jiffies(200));

/*Schedule delayed work in the MMC workqueue.调度延时工作队列*/

àmmc_schedule_delayed_work(&host->detect, delay); /* host->detect为mmc_rescan*/

 

搜索host->detected得到以下信息:drivers/mmc/core/host.c

INIT_DELAYED_WORK(&host->detect, mmc_rescan);

mmc_rescan(struct work_struct *work)

 

mmc_rescan

àmmc_bus_put(host);//card 从bus上移除时,释放它占有的总线空间

/*mmc_claim_host判断当前mmc host控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0

*如果为1,那么会在while(1)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 *mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的使用权

*/

àmmc_claim_host(host);

àmmc_rescan_try_freq(host, max(freqs[i], host->f_min);

/**/

static intmmc_rescan_try_freq(struct mmc_host *host, unsigned freq)

{

     …………………………………………..

     /* Order's important: probe SDIO, then SD,then MMC */

    if(!mmc_attach_sdio(host))

         return 0;

     if (!mmc_attach_sd(host))

         return 0;

     if (!mmc_attach_mmc(host))

         return 0;

………………………………………….

}

 

mmc_rescan_try_freq

àmmc_attach_sdio(struct mmc_host *host)  //匹配sdio接口卡

         àmmc_send_io_op_cond

àmmc_attach_bus(host, &mmc_sdio_ops);

/*当card与总线上的驱动匹配,即mmc_attach_bus成功返回,就初始化card*/

àmmc_select_voltage(host, ocr);

àmmc_sdio_init_card(host, host->ocr, NULL, 0);

àcard =mmc_alloc_card(host, NULL);//分配一个mmc_card结构体

àmmc_set_bus_mode(host,MMC_BUSMODE_PUSHPULL); //设置mmc_bus的工作模式

àsdio_init_func(host->card, i + 1); // 初始化sdio_func结构体 struct sdio_func  *sdio_func[SDIO_MAX_FUNCS]; //SDIO functions (devices)

              à structsdio_func *func = sdio_alloc_func(card); //分配struct sdio_fun(sdio功能设备)结构体

                            àfunc = kzalloc(sizeof(structsdio_func), GFP_KERNEL);

                            àdevice_initialize(&func->dev);

à sdio_read_fbr(func);

à sdio_read_func_cis(func);

à card->sdio_func[fn - 1] = func;

                   à mmc_add_card(host->card); // 将具体的sdio设备挂载到mmc_bus_types 总线

àsdio_add_func(host->card->sdio_func[i]);//将sdio功能设备挂载到sdio_bus_types总线         àdev_set_name(&func->dev,"%s:%d", mmc_card_id(func->card), func->num);

à device_add(&func->dev);

                            àsdio_func_set_present(func);

mmc_card结构体中sdio_func[]赋值为分配的sdio_func结构体,通过sdio_func结构体与wireless驱动层建立联系

 

抱歉!评论已关闭.