在linux-3.6.6中,SD驱动很完整,无需添加任何代码,只需修改一下menuconfig配置即可使用。

 

在arch/arm/plat-samsung/devs.c文件内定义了SD平台设备:

static struct resource s3c_sdi_resource[] = {

       [0] = DEFINE_RES_MEM(S3C24XX_PA_SDI,S3C24XX_SZ_SDI),

        [1] = DEFINE_RES_IRQ(IRQ_SDI),

};

 

struct platform_device s3c_device_sdi = {

       .name           = "s3c2410-sdi",

       .id             = -1,

       .num_resources  = ARRAY_SIZE(s3c_sdi_resource),

       .resource       = s3c_sdi_resource,

};

 

在arch/arm/mach-s3c24xx/mach-zhaocj2440.c文件内添加了该设备,即在结构数组zhaocji2440_devices中了有SD平台设备:

&s3c_device_sdi,

 

另外在zhaocj2440_init函数内,还设置了基于s3c2440的SD控制器的平台数据:

s3c24xx_mci_set_platdata(&zhaocj2440_mmc_cfg);

 

其中zhaocj2440_mmc_cfg定义为:

static struct s3c24xx_mci_pdata zhaocj2440_mmc_cfg __initdata = {

  .gpio_detect   = S3C2410_GPG(8),            //用于探测SD卡的GPIO引脚

  .gpio_wprotect = S3C2410_GPH(8),             //用于写保护的GPIO引脚

  .set_power     = NULL,                     //用于控制电源模式的回调函数

  .ocr_avail     =MMC_VDD_32_33|MMC_VDD_33_34,         //SD卡的电源电压设置

};

 

下面就重点介绍SD的平台驱动。由于该内容较长,我们把它分为两部分,第一部分重点介绍SD卡插拔的检测,第二部分介绍SD卡的读写

 

SD设备被当做是块设备,它的驱动分为三个层次:host层,它是负责SD控制器;card层,它负责具体的SD卡操作,这两层分别有一个对应相关的结构体——mmc_host和mmc_card;最后一层是core层,它连接了host层和card层。更具体一点就是:SD卡 -> card层 -> core层 -> host层 -> s3c2440上的SD控制器。系统用s3cmci_host结构体来表示s3c2440的SD控制器

 

我们先来看针对s3c2440的host层文件——drivers/mmc/host/s3cmci.c。系统通过下列语句注册了一个SD驱动:

module_platform_driver(s3cmci_driver);

 

s3cmci_driver的定义为:

static struct platform_driver s3cmci_driver= {

       .driver = {

                .name   = "s3c-sdi",

               .owner  = THIS_MODULE,

                .pm     = s3cmci_pm_ops,

       },

       .id_table       =s3cmci_driver_ids,

       .probe          = s3cmci_probe,

       .remove         = s3cmci_remove,

       .shutdown       = s3cmci_shutdown,

};

 

而驱动列表s3cmci_driver_ids定义为:

static struct platform_device_ids3cmci_driver_ids[] = {

       {

                .name   = "s3c2410-sdi",

                .driver_data    = 0,

       }, {

                .name   = "s3c2412-sdi",

                .driver_data    = 1,

       }, {

                .name   = "s3c2440-sdi",

                .driver_data    = 1,

       },

       { }

};

 

其中"s3c2410-sdi"与上面定义的SD平台设备的名字一致,因此系统自动执行s3cmci_probe函数:

static int s3cmci_probe(structplatform_device *pdev)

{

       struct s3cmci_host *host;

       struct mmc_host *mmc;

       int ret;

       int is2440;

       int i;

 

       //判断是否是2440,从上面给出的s3cmci_driver_ids可以看出,is2440为0

       is2440 = platform_get_device_id(pdev)->driver_data;

 

       //由s3cmci_host结构,分配一个mmc_host结构,

       //即为s3c2440的SD控制器建立一个mmc子系统的host数据结构

       mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);

       if (!mmc) {

                ret = -ENOMEM;

                goto probe_out;

       }

 

       //为SD申请GPE5至GPE10引脚

       for (i = S3C2410_GPE(5);i <= S3C2410_GPE(10); i++) {

                ret = gpio_request(i,dev_name(&pdev->dev));

                if (ret) {

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

 

                        for (i--; i >= S3C2410_GPE(5); i--)

                                gpio_free(i);

 

                        goto probe_free_host;

                }

       }

 

       //为s3c2440的SD控制器s3cmci_host结构体赋值

       host =mmc_priv(mmc);              //指向host的private域

        host->mmc       = mmc;

       host->pdev      = pdev;

       host->is2440    = is2440;

 

       //平台数据,即zhaocj2440_mmc_cfg内容

       host->pdata= pdev->dev.platform_data;

        if(!host->pdata) {

                pdev->dev.platform_data =&s3cmci_def_pdata;

                host->pdata =&s3cmci_def_pdata;

       }

 

       //初始化自旋锁

       spin_lock_init(&host->complete_lock);

       //动态创建host->pio_tasklet,SD传输中断的下半部分

       tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long)host);

 

       if (is2440) {

                host->sdiimsk   = S3C2440_SDIIMSK;

                host->sdidata   = S3C2440_SDIDATA;

                host->clk_div   = 1;

       } else {

                host->sdiimsk   = S3C2410_SDIIMSK;

                host->sdidata   = S3C2410_SDIDATA;

                host->clk_div   = 2;

       }

 

       //记录请求处理所处的当前状态

       host->complete_what     =COMPLETION_NONE;

       //请求处理数据在FIFO方式下的数据方向是读还是写

       host->pio_active        =XFER_NONE;

 

#ifdef CONFIG_MMC_S3C_PIODMA

       //是否使用DMA

       host->dodma             =host->pdata->use_dma;

#endif

 

       //取得内存资源

       host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

        if (!host->mem) {

                dev_err(&pdev->dev,

                        "failed to get iomemory region resource.\n");

 

                ret = -ENOENT;

                goto probe_free_gpio;

       }

 

       //申请映射

       host->mem = request_mem_region(host->mem->start,

                                      resource_size(host->mem), pdev->name);

 

       if (!host->mem) {

                dev_err(&pdev->dev,"failed to request io memory region.\n");

                ret = -ENOENT;

                goto probe_free_gpio;

       }

 

       //虚拟基址

       host->base = ioremap(host->mem->start,resource_size(host->mem));

       if (!host->base) {

                dev_err(&pdev->dev,"failed to ioremap() io memory region.\n");

                ret = -EINVAL;

                goto probe_free_mem_region;

       }

 

       //取得中断号

       host->irq = platform_get_irq(pdev, 0);

       if (host->irq == 0) {

                dev_err(&pdev->dev,"failed to get interrupt resource.\n");

                ret = -EINVAL;

                goto probe_iounmap;

       }

 

       //申请中断,该中断为读写SD卡数据时所产生的中断

       if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {

                dev_err(&pdev->dev,"failed to request mci interrupt.\n");

                ret = -ENOENT;

                goto probe_iounmap;

       }

 

       /* We get spurious interrupts even when we have set the IMSK

        * register to ignore everything, so use disable_irq() to make

        * ensure we don't lock the system with un-serviceable requests. */

 

       //禁止上面所申请的中断

       disable_irq(host->irq);

       host->irq_state = false;

       

       if (!host->pdata->no_detect) {      //如果SD控制器具有检测SD卡插拔状态的功能

                //申请检测SD卡插拔状态所需要的GPIO,该引脚在zhaocj2440_mmc_cfg结构中被定义

                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;

                }

 

                //为检测SD卡的插拔状态获取中断号

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

 

                if (host->irq_cd >= 0) {

                        //申请中断,当有SD卡插入或拔出时,则进入该中断

                        if(request_irq(host->irq_cd, s3cmci_irq_cd,

                                       IRQF_TRIGGER_RISING |

                                       IRQF_TRIGGER_FALLING,

                                       DRIVER_NAME, host)) {

                               dev_err(&pdev->dev,

                                       "can't get card detect irq.\n");

                                ret = -ENOENT;

                                gotoprobe_free_gpio_cd;

                        }

                } else {

                       dev_warn(&pdev->dev,

                                 "hostdetect has no irq available\n");

                       gpio_direction_input(host->pdata->gpio_detect);

                }

       } else             //如果SD控制器不具有检测SD卡插拔状态的功能

                host->irq_cd = -1;

 

       if (!host->pdata->no_wprotect) {  //如果SD控制器具有写保护的功能

               //申请SD卡写保护所需要的GPIO,该引脚在zhaocj2440_mmc_cfg结构中被定义

                ret =gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");

                if (ret) {

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

                        goto probe_free_irq_cd;

                }

 

                //设置该引脚为输入

               gpio_direction_input(host->pdata->gpio_wprotect);

       }

 

       /* depending on the dma state, get a dma channel to use. */

       //如果使用DMA,则要获取DMA通道

       if (s3cmci_host_usedma(host)) {

                host->dma = s3c2410_dma_request(DMACH_SDI,&s3cmci_dma_client,

                                               host);

                if (host->dma < 0) {

                       dev_err(&pdev->dev, "cannot get DMA channel.\n");

                        if(!s3cmci_host_canpio()) {

                                ret = -EBUSY;

                                gotoprobe_free_gpio_wp;

                        } else {

                               dev_warn(&pdev->dev, "falling back to PIO.\n");

                                host->dodma= 0;

                        }

                }

       }

 

       //获取时钟信号

       host->clk = clk_get(&pdev->dev, "sdi");

       if (IS_ERR(host->clk)) {

                dev_err(&pdev->dev,"failed to find clock source.\n");

                ret = PTR_ERR(host->clk);

                host->clk = NULL;

                goto probe_free_dma;

       }

 

       //使能该时钟

       ret = clk_enable(host->clk);

       if (ret) {

               dev_err(&pdev->dev,"failed to enable clock source.\n");

                goto clk_free;

       }

 

       //通过时钟信号源获取时钟频率

       host->clk_rate = clk_get_rate(host->clk);

 

       //为mmc_host结构赋值

       mmc->ops        =&s3cmci_ops;           //SD控制器的操作函数

       mmc->ocr_avail  = MMC_VDD_32_33| MMC_VDD_33_34;         //可利用的电压范围

#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ

       mmc->caps       =MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;

#else

       mmc->caps       =MMC_CAP_4_BIT_DATA;

#endif

       mmc->f_min      =host->clk_rate / (host->clk_div * 256);    //最小工作频率

       mmc->f_max      =host->clk_rate / host->clk_div;        //最大工作频率

 

       //如果在zhaocj2440_mmc_cfg结构中定义了ocr_avail,则以该结构体定义的为准

       if (host->pdata->ocr_avail)

                mmc->ocr_avail = host->pdata->ocr_avail;

 

        mmc->max_blk_count      = 4095;          //一次请求的最大block数

        mmc->max_blk_size       = 4095;          //block的最大值

        mmc->max_req_size       = 4095 * 512;        //一次请求的最大字节数

        mmc->max_seg_size       = mmc->max_req_size;         //最大块大小

 

       mmc->max_segs           = 128;           //最多块

 

       dbg(host, dbg_debug,

           "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%udma:%u.\n",

           (host->is2440?"2440":""),

           host->base, host->irq, host->irq_cd, host->dma);

 

       ret = s3cmci_cpufreq_register(host);

       if (ret) {

                dev_err(&pdev->dev,"failed to register cpufreq\n");

                goto free_dmabuf;

       }

 

       //将mmc添加进系统

       ret =mmc_add_host(mmc);

        if (ret) {

                dev_err(&pdev->dev,"failed to add mmc host.\n");

                goto free_cpufreq;

       }

 

       s3cmci_debugfs_attach(host);

 

       //将SD设备的数据赋值给系统平台设备

       platform_set_drvdata(pdev, mmc);

       dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n",mmc_hostname(mmc),

                 s3cmci_host_usedma(host) ?"dma" : "pio",

                 mmc->caps & MMC_CAP_SDIO_IRQ ? "hw": "sw");

 

       return 0;

 

 free_cpufreq:

       s3cmci_cpufreq_deregister(host);

 

 free_dmabuf:

       clk_disable(host->clk);

 

 clk_free:

       clk_put(host->clk);

 

 probe_free_dma:

       if (s3cmci_host_usedma(host))

                s3c2410_dma_free(host->dma, &s3cmci_dma_client);

 

 probe_free_gpio_wp:

       if (!host->pdata->no_wprotect)

                gpio_free(host->pdata->gpio_wprotect);

 

 probe_free_gpio_cd:

       if (!host->pdata->no_detect)

               gpio_free(host->pdata->gpio_detect);

 

 probe_free_irq_cd:

       if (host->irq_cd >= 0)

                free_irq(host->irq_cd,host);

 

 probe_free_irq:

       free_irq(host->irq, host);

 

 probe_iounmap:

       iounmap(host->base);

 

 probe_free_mem_region:

       release_mem_region(host->mem->start, resource_size(host->mem));

 

 probe_free_gpio:

       for (i = S3C2410_GPE(5);i <= S3C2410_GPE(10); i++)

                gpio_free(i);

 

 probe_free_host:

       mmc_free_host(mmc);

 

 probe_out:

       return ret;

}

 

其中s3cmci_ops定义为:

static struct mmc_host_ops s3cmci_ops = {

       .request        = s3cmci_request,             //实现命令和数据的传输

       .set_ios        = s3cmci_set_ios,              //设置硬件的IO

       .get_ro         = s3cmci_get_ro,             //判断SD卡是否写保护

       .get_cd         =s3cmci_card_present,    //判断SD卡是否存在

       .enable_sdio_irq = s3cmci_enable_sdio_irq,

};

 

从对s3cmci_probe函数的分析可以看出,该函数除了对s3cmci_host和mmc_host这两个结构体赋值外,最重要的工作就是调用了mmc_alloc_host函数和mmc_add_host函数。前者分配一个mmc_host,后者添加一个mmc_host。下面我们就来介绍这两个函数,这两个函数都在drivers/mmc/core/host.c文件内。

 

struct mmc_host *mmc_alloc_host(int extra,struct device *dev)

{

       int err;

       struct mmc_host *host;

 

       if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))

                return NULL;

 

       //分配内存空间

       host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);

       if (!host)

                return NULL;

 

       /* scanning will be enabled when we're ready */

       //关闭自动扫描SD的功能

       host->rescan_disable = 1;

       spin_lock(&mmc_host_lock);

       //分配新的idr入口

       err = idr_get_new(&mmc_host_idr, host, &host->index);

       spin_unlock(&mmc_host_lock);

       if (err)

                goto free;

       //设置设备的名字

       dev_set_name(&host->class_dev, "mmc%d",host->index);

 

       host->parent = dev;

       host->class_dev.parent = dev;

       host->class_dev.class = &mmc_host_class;

       //初始化设备结构体

       device_initialize(&host->class_dev);

       //设置时钟

       mmc_host_clk_init(host);

 

       mutex_init(&host->slot.lock);

       host->slot.cd_irq = -EINVAL;

 

       spin_lock_init(&host->lock);

       //初始化等待队列,工作队列

       init_waitqueue_head(&host->wq);

       INIT_DELAYED_WORK(&host->detect, mmc_rescan);

#ifdef CONFIG_PM

       host->pm_notify.notifier_call = mmc_pm_notify;

#endif

 

       /*

        * By default, hosts do not support SGIO or large requests.

        * They have to set these according to their abilities.

         */

       host->max_segs = 1;

       host->max_seg_size = PAGE_CACHE_SIZE;

 

       host->max_req_size = PAGE_CACHE_SIZE;

       host->max_blk_size = 512;

       host->max_blk_count = PAGE_CACHE_SIZE / 512;

 

       return host;

 

free:

       kfree(host);

       return NULL;

}

 

int mmc_add_host(struct mmc_host *host)

{

       int err;

 

       WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&

               !host->ops->enable_sdio_irq);

       //添加设备

       err = device_add(&host->class_dev);

        if (err)

                return err;

 

       led_trigger_register_simple(dev_name(&host->class_dev),&host->led);

 

#ifdef CONFIG_DEBUG_FS

       mmc_add_host_debugfs(host);

#endif

       mmc_host_clk_sysfs_init(host);

       //启动SD

       mmc_start_host(host);

       register_pm_notifier(&host->pm_notify);

 

       return 0;

}

 

在mmc_add_host函数内,调用mmc_start_host函数来启动SD。在mmc_start_host函数内,首先host->rescan_disable = 0;,来使能SD卡扫描功能,然后调用mmc_detect_change函数来检测是否有SD卡已经插入。mmc_detect_change函数又调用mmc_schedule_delayed_work函数,mmc_schedule_delayed_work函数又调用了queue_delayed_work函数。这样经过一段延时后,会执行在mmc_alloc_host函数中定义的工作队列(INIT_DELAYED_WORK(&host->detect,mmc_rescan);)所指向的mmc_rescan函数。为什么要延时,这和按键去抖延时相类似。但在这里,检测SD卡是在系统初始化的过程中,也就是说SD卡早已经插入插槽,机器才上电,因此在这里是不需要延时的。上面用到的函数都是在drivers/mmc/core/core.c文件内定义的。

 

下面我们来介绍mmc_rescan函数,它用来具体执行扫描并检测是否有SD卡插入的:

void mmc_rescan(struct work_struct *work)

{

       struct mmc_host *host =

                container_of(work, structmmc_host, detect.work);

       int i;

 

       if (host->rescan_disable)

                return;

 

       /* If there is a non-removable card registered, only scan once */

       //如果已有一个不能移动的卡注册过,并扫描过一次,则不用再扫描了

       if ((host->caps & MMC_CAP_NONREMOVABLE) &&host->rescan_entered)

                return;

       host->rescan_entered = 1;

       //引用计数加1

       mmc_bus_get(host);

 

       /*

        * if there is a _removable_ card registered, check whether it is

        * still present

        */

       //检测SD卡的拔出

       if (host->bus_ops && host->bus_ops->detect &&!host->bus_dead

           && !(host->caps & MMC_CAP_NONREMOVABLE))

               host->bus_ops->detect(host);

 

       host->detect_change = 0;

 

       /*

        * Let mmc_bus_put() free the bus/bus_ops if we've found that

        * the card is no longer present.

        */

       mmc_bus_put(host);

       mmc_bus_get(host);

 

       /* if there still is a card present, stop here */

       // host->bus_ops != NULL,说明这次mmc_rescan不是在上电初始化时调用的

       //所以不需要执行if后面的内容,要直接执行mmc_schedule_delayed_work函数

       if (host->bus_ops != NULL) {

                mmc_bus_put(host);

                goto out;

       }

 

       /*

        * Only we can add a new handler, so it's safe to

        * release the lock here.

        */

       mmc_bus_put(host);

       //通过host->ops->get_cd,即s3cmci_card_present函数,检测SD卡是否存在

       if (host->ops->get_cd && host->ops->get_cd(host) ==0) {              //SD卡不存在

                mmc_claim_host(host);         //判断当前mmc控制器是否被占用

                mmc_power_off(host);         //断电

                mmc_release_host(host);              //释放SD控制器

                goto out;

       }

 

       mmc_claim_host(host);

       for (i = 0; i < ARRAY_SIZE(freqs); i++) {

                //利用不同的频率探测SD卡,freqs在该文件头部被定义为

                // static const unsignedfreqs[] = { 400000, 300000, 200000, 100000 };

                if (!mmc_rescan_try_freq(host,max(freqs[i], host->f_min)))

                        break;

                if (freqs[i] <= host->f_min)

                        break;

       }

       mmc_release_host(host);

 

 out:

       if (host->caps & MMC_CAP_NEEDS_POLL)

               mmc_schedule_delayed_work(&host->detect, HZ);

}

 

下面再来分析mmc_rescan_try_freq函数:

static int mmc_rescan_try_freq(structmmc_host *host, unsigned freq)

{

       host->f_init = freq;

 

#ifdef CONFIG_MMC_DEBUG

       pr_info("%s: %s: trying to init card at %u Hz\n",

                mmc_hostname(host), __func__,host->f_init);

#endif

       mmc_power_up(host);          //上电

 

        /*

        * Some eMMCs (with VCCQ always on) may not be reset after power up, so

        * do a hardware reset if possible.

        */

       mmc_hw_reset_for_init(host);             //硬件复位

 

       /*

        * sdio_reset sends CMD52 to reset card. Since we do not know

        * if the card is being re-initialized, just send it.  CMD52

        * should be ignored by SD/eMMC cards.

        */

       sdio_reset(host);            //SDIO设备复位

       //发送命令CMD0,使其处于IDLE状态

       mmc_go_idle(host);

       //发送SD_SEND_IF_COND命令,以支持SD2.0卡

       mmc_send_if_cond(host, host->ocr_avail);

 

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

       //依次探测SDIO、SD、MMC

       if (!mmc_attach_sdio(host))

                return 0;

       if (!mmc_attach_sd(host))

                return 0;

       if (!mmc_attach_mmc(host))

                return 0;

 

       mmc_power_off(host);

       return -EIO;

}

 

我们以SD卡为例,因此会调用mmc_attach_sd函数,它实现了SD卡的初始化:

int mmc_attach_sd(struct mmc_host *host)

{

       int err;

       u32 ocr;

 

       BUG_ON(!host);

       WARN_ON(!host->claimed);

 

       /* Disable preset value enable if already set since last time */

       if (host->ops->enable_preset_value) {

                mmc_host_clk_hold(host);

                host->ops->enable_preset_value(host,false);

                mmc_host_clk_release(host);

       }

 

       //发送SD_APP_OP_COND命令

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

       if (err)

                return err;

 

       //注册SD总线

       mmc_sd_attach_bus_ops(host);

       //设置SD卡电压范围

       if (host->ocr_avail_sd)

                host->ocr_avail = host->ocr_avail_sd;

 

        /*

        * We need to get OCR a different way for SPI.

        */

       if (mmc_host_is_spi(host)) {

                mmc_go_idle(host);

 

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

                if (err)

                        goto err;

       }

 

       /*

        * Sanity check the voltages that the card claims to

        * support.

        */

       if (ocr & 0x7F){

                pr_warning("%s: cardclaims to support voltages "

                      "below thedefined range. These will be ignored.\n",

                       mmc_hostname(host));

                ocr &= ~0x7F;

       }

 

       if ((ocr & MMC_VDD_165_195) &&

           !(host->ocr_avail_sd & MMC_VDD_165_195)) {

               pr_warning("%s: SD cardclaims to support the "

                       "incompletelydefined 'low voltage range'. This "

                       "will beignored.\n", mmc_hostname(host));

                ocr &= ~MMC_VDD_165_195;

       }

 

       //设置电压,最终调用的是s3cmci_set_ios函数

       host->ocr = mmc_select_voltage(host, ocr);

 

       /*

        * Can we support the voltage(s) of the card(s)?

        */

       if (!host->ocr) {

                err = -EINVAL;

                goto err;

        }

 

       /*

        * Detect and init the card.

        */

       //启动SD卡

       err = mmc_sd_init_card(host, host->ocr, NULL);

       if (err)

                goto err;

 

       mmc_release_host(host);

       //注册一个新的SD卡

       err = mmc_add_card(host->card);

       mmc_claim_host(host);

       if (err)

                goto remove_card;

 

       return 0;

 

remove_card:

       mmc_release_host(host);

       mmc_remove_card(host->card);

       host->card = NULL;

       mmc_claim_host(host);

err:

       mmc_detach_bus(host);

 

       pr_err("%s: error %d whilst initialising SD card\n",

                mmc_hostname(host), err);

 

       return err;

}

 

我们再来详细介绍几个在mmc_attach_sd函数中调用的函数。mmc_sd_attach_bus_ops函数调用了mmc_attach_bus函数,在mmc_attach_bus函数内,把mmc_sd_ops赋给了mmc_host->bus_ops:

static const struct mmc_bus_ops mmc_sd_ops= {

       .remove = mmc_sd_remove,         //拔出SD卡的操作

       .detect = mmc_sd_detect,             //探测SD卡的操作

       .suspend = NULL,

       .resume = NULL,

       .power_restore = mmc_sd_power_restore,

       .alive = mmc_sd_alive,

};

 

在mmc_sd_init_card函数内,会通过发送一系列命令来启动SD卡,并得到寄存器CID,CSD,SCR,RCA等的数据。mmc_attach_sd函数内调用的另一个重要函数是mmc_add_card,它通过调用device_add函数把mmc_card设备添加进系统中,并通知mmc块设备驱动。关于mmc块设备,我们在第二部分再介绍。

 

至此,mmc子系统的启动,及检测SD卡设备就完成了。这个SD卡的检测是在上电之前就已插入插槽的,那么如果上电以后再把SD卡插入插槽中,系统又是如何检测的呢?

 

在前面介绍s3cmci_probe函数的时候,申请了两个中断,一个用于数据传输,另一个就是用于检测SD卡的。也就是当SD卡插入时,会引起相关引脚的电平变化,从而触发该中断,进入中断处理函数s3cmci_irq_cd:

static irqreturn_t s3cmci_irq_cd(int irq,void *dev_id)

{

       struct s3cmci_host *host = (struct s3cmci_host *)dev_id;

 

       dbg(host, dbg_irq, "card detect\n");

 

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

 

       return IRQ_HANDLED;

}

 

mmc_detect_change函数前面介绍过,它是用来检测是否存在SD卡。它的第二个参数表示延时的时间。在这里一定是要有延时的。调用mmc_detect_change函数后所执行的内容与前面介绍的一样,这里就不再重复了。

 

无论是上电之前还是上电之后,SD卡的插入检测我们在上面都详细介绍完了,那么SD卡的拔出是如何检测的呢?当然一定是带电状态下才能检测SD卡的拔出的。

 

还是回到mmc_rescan函数中:

void mmc_rescan(struct work_struct *work)

{

       ……

       if (host->bus_ops && host->bus_ops->detect &&!host->bus_dead

           && !(host->caps & MMC_CAP_NONREMOVABLE))

               host->bus_ops->detect(host);

       ……

}

 

上面的语句就是用来检测SD卡的拔出的。我们在前面也已经介绍过了,在mmc_attach_bus函数内,把mmc_sd_ops赋给了mmc_host->bus_ops,所以host->bus_ops->detect(host);实际回调的是mmc_sd_detect函数。这里可能会有一个疑问:mmc_attach_bus函数是在mmc_rescan函数之后被调用的,那么就是说在mmc_rescan函数内,mmc_host->bus_ops还没有被赋值,所以就不会回调mmc_sd_detect函数了。我们要注意一点的是,mmc_rescan函数是在上电初始化过程中会被执行一次,因此在初始化的过程中完成了mmc_host->bus_ops的赋值。而检测SD卡的拔出一定是在完成初始化以后,这时mmc_host->bus_ops已经被赋值,所以执行mmc_rescan函数中的host->bus_ops->detect(host);语句不会出现任何问题。

 

我们来看看回调函数mmc_sd_detect,它定义在drivers/mmc/core/sd.c文件内:

static void mmc_sd_detect(struct mmc_host*host)

{

       int err;

 

       BUG_ON(!host);

       BUG_ON(!host->card);

 

       mmc_claim_host(host);

 

       /*

        * Just check if our card has been removed.

        */

       err = _mmc_detect_card_removed(host);

 

       mmc_release_host(host);

 

       if (err) {

                mmc_sd_remove(host);

 

                mmc_claim_host(host);

                mmc_detach_bus(host);

                mmc_power_off(host);

                mmc_release_host(host);

       }

}

 

上面的函数主要调用了_mmc_detect_card_removed函数:

int _mmc_detect_card_removed(structmmc_host *host)

{

       int ret;

 

       if ((host->caps & MMC_CAP_NONREMOVABLE) ||!host->bus_ops->alive)

                return 0;

       //SD卡不存在,或SD卡的状态为已移走,则退出

       if (!host->card || mmc_card_removed(host->card))

                return 1;

       //调用mmc_sd_alive函数,再调用mmc_send_status函数,得到SD卡状态信息

       //得到状态数据,返回0;否则出错,返回非0

       ret = host->bus_ops->alive(host);

       if (ret) {

                //标注SD卡为已移走

               mmc_card_set_removed(host->card);

                pr_debug("%s: card removedetected\n", mmc_hostname(host));

       }

 

       return ret;

}

 

为什么要用给SD卡发送状态信息来判断SD卡是否存活(即是否被拔出)?因为当把SD卡拔出时,当然是得不到任何SD卡的信息,所以会出错。我们再回到mmc_sd_detect函数内,如果SD卡已拔出,_mmc_detect_card_removed函数返回非零值,则执行下面的if里的内容。拔出SD卡与插入SD卡的过程相反,这里就不再详细讲解mmc_card_set_removed函数。

 

下面我们来验证一下SD驱动。把SD卡插入开发板的插槽中,上电启动开发板,如果系统能够打印出类似下面的信息,则说明开发板能够识别出SD卡:

s3c-sdis3c2440-sdi: running at 400kHz(requested: 400kHz).

s3c-sdis3c2440-sdi: running at 400kHz(requested: 400kHz).

s3c-sdis3c2440-sdi: running at 400kHz(requested: 400kHz).

s3c-sdis3c2440-sdi: running at 400kHz(requested: 400kHz).

s3c-sdis3c2440-sdi: running at 400kHz(requested: 400kHz).

s3c-sdis3c2440-sdi: running at25000kHz (requested: 25000kHz).

s3c-sdis3c2440-sdi: running at25000kHz (requested: 25000kHz).

mmc0: new SDHC card at address aaaa

mmcblk0: mmc0:aaaa SD08G 7.40 GiB

mmcblk0: p1

如果先把开发板上电,然后再把SD卡插入插槽中,则系统也会自动打印出上面的信息。如果我们在带电的情况下把SD卡拔出,则系统会自动打印出类似下面的信息:

mmc0: card aaaa removed

s3c-sdis3c2440-sdi: powered down.

 

下面我们用命令来操作SD卡:

显示SD卡

[root@zhaocj/]#ls   /dev/mmc*

/dev/mmcblk0    /dev/mmcblk0p1

我们也可以通过下面的命令在查看SD卡的情况

[root@zhaocj /]#cat   /proc/partitions

major    minor     #blocks  name

 31        0        512 mtdblock0

 31        1        512 mtdblock1

 31        2       6144 mtdblock2

 31        3     253952 mtdblock3

 179       0    7761920 mmcblk0

 179       1    7757824 mmcblk0p1

在/temp目录下创建/mnt/sd目录,用来挂载SD卡

[root@zhaocj/]#mkdir   /temp/mnt

[root@zhaocj/]#mkdir   /temp/mnt/sd

挂载SD卡

[root@zhaocj /]#mount   /dev/mmcblk0p1   /temp/mnt/sd

[root@zhaocj /]#ls   /temp/mnt/sd

这时会显示出SD卡的文件及目录

下面就来卸载SD卡

[root@zhaocj /]#umount   /temp/mnt/sd/

 

如果在挂载SD卡的时候,显示这样的错误信息:

FAT-fs (mmcblk0p1): codepage cp437 notfound

则需要在配置linux内核时,选择下面的选项:

File  systems --->

     -*- Native  language  support--->

          <*>   Codepage   437   (United States,Canada)

又如果在挂载SD卡的时候,显示下面的错误信息:

FAT-fs (mmcblk0p1): IO charset iso8859-1not found

则还需要在配置linux内核时,选择下面的选项:

File  systems --->

     -*- Native  language  support--->

          <*>   NLS  ISO 8859-1  (Latin 1; Western EuropeanLanguages)

 

如果挂载SD卡的时候,显示下面的错误信息:

mount: mounting /dev/mmcblk0p1 on/temp/mnt/sd/ failed: Invalid argument

则可能是SD卡没有格式化,或linux系统不支持SD卡的文件格式。通过cat /proc/filesystems命令可以查看到当前linux系统所支持的文件格式,把SD卡格式化成linux系统所支持的文件格式,也许就不会出现上述的错误。
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐