基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)

在阅读本文之前,请先掌握以下基本知识,不然请略过本文。

预备知识

熟读LDD3前十章节的内容。

熟悉内核驱动模型(sysfs)和platform总线。

简要了解过SD卡规范。


本文的内容基于如下硬件和软件平台:

目标平台:TQ2440

CPU:s3c2440

内核版本:3.12.5

基于SD规范4.10,即《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10》。


在阅读MMC子系统时,一个问题随之就会产生:当我们插入一张SD卡时,系统是如何识别到这张SD卡并将它注册进系统的呢?

这一过程,源于MMC控制器驱动的不懈努力。。。。。。下面,我们从控制器驱动开始,来深入挖掘这一过程。

1. MMC控制器驱动

1.1 MMC控制器入口函数及probe方法

本文以三星的s3c2440上的MMC控制器驱动为例,来进行简要的说明。

从MMC控制器驱动的入口函数开始,如下:

下列代码位于:linux/drivers/mmc/host/s3cmci.c

[cpp] view plain copy print ?
  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     = s3cmci_remove,  
  10.     .shutdown   = s3cmci_shutdown,  
  11. };  
  12.   
  13. module_platform_driver(s3cmci_driver);  

这里直接调用了platform的驱动注册函数来注册一个名为s3c-sdi的驱动,该驱动将绑定mmc主控制器设备。

为了让该驱动成功绑定MMC主控制器设备,需要先进行移植操作,具体可见:S3C2440 Linux驱动移植——SD卡驱动

绑定成功后,立即会调用probe方法,也就是s3cmci_probe函数,我们来看下:

[cpp] view plain copy print ?
  1. static int s3cmci_probe(struct platform_device *pdev)  
  2. {  
  3.     struct s3cmci_host *host;  
  4.     struct mmc_host *mmc;  
  5.     int ret;  
  6.     int is2440;  
  7.     int i;  
  8.   
  9.     /* */  
  10.     is2440 = platform_get_device_id(pdev)->driver_data;  
  11.   
  12.     /* 分配struct mmc_host和struct s3cmci_host */  
  13.     mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);  
  14.     if (!mmc) {  
  15.         ret = -ENOMEM;  
  16.         goto probe_out;  
  17.     }  
  18.   
  19.     /* 申请IO管脚*/  
  20.     for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {  
  21.         ret = gpio_request(i, dev_name(&pdev->dev));  
  22.         if (ret) {  
  23.             dev_err(&pdev->dev, "failed to get gpio %d\n", i);  
  24.   
  25.             for (i--; i >= S3C2410_GPE(5); i--)  
  26.                 gpio_free(i);  
  27.   
  28.             goto probe_free_host;  
  29.         }  
  30.     }  
  31.   
  32.     host = mmc_priv(mmc);   /* 获取struct s3cmci_host指针*/  
  33.     host->mmc    = mmc;  /* 保存mmc指针*/  
  34.     host->pdev   = pdev; /* 保存平台设备指针*/  
  35.     host->is2440 = is2440; /* 是否为2440*/  
  36.   
  37.     host->pdata = pdev->dev.platform_data; /* 保存板级设备信息*/  
  38.     if (!host->pdata) {  
  39.         /* 如果没有板级设备信息,给出默认的*/  
  40.         pdev->dev.platform_data = &s3cmci_def_pdata;  
  41.         host->pdata = &s3cmci_def_pdata;  
  42.     }  
  43.   
  44.     /* 初始化自旋锁*/  
  45.     spin_lock_init(&host->complete_lock);  
  46.     /* 初始化1个tasklet,执行pio_tasklet*/  
  47.     tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);  
  48.   
  49.     if (is2440) {  
  50.         host->sdiimsk    = S3C2440_SDIIMSK;  
  51.         host->sdidata    = S3C2440_SDIDATA;  
  52.         host->clk_div    = 1;  
  53.     } else {  
  54.         host->sdiimsk    = S3C2410_SDIIMSK;  
  55.         host->sdidata    = S3C2410_SDIDATA;  
  56.         host->clk_div    = 2;  
  57.     }  
  58.   
  59.     host->complete_what  = COMPLETION_NONE;  
  60.     host->pio_active     = XFER_NONE;  
  61.   
  62. /* 板级数据可以决定是否使用DMA,也可以通过配置来决定*/  
  63. #ifdef CONFIG_MMC_S3C_PIODMA  
  64.     host->dodma      = host->pdata->use_dma;    
  65. #endif  
  66.   
  67.     /* 获取寄存器资源*/  
  68.     host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  69.     if (!host->mem) {  
  70.         dev_err(&pdev->dev,  
  71.             "failed to get io memory region resource.\n");  
  72.   
  73.         ret = -ENOENT;  
  74.         goto probe_free_gpio;  
  75.     }  
  76.     /* 申请IO内存空间*/  
  77.     host->mem = request_mem_region(host->mem->start,  
  78.                        resource_size(host->mem), pdev->name);  
  79.   
  80.     if (!host->mem) {  
  81.         dev_err(&pdev->dev, "failed to request io memory region.\n");  
  82.         ret = -ENOENT;  
  83.         goto probe_free_gpio;  
  84.     }  
  85.   
  86.     /* 映射寄存器*/  
  87.     host->base = ioremap(host->mem->start, resource_size(host->mem));  
  88.     if (!host->base) {  
  89.         dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");  
  90.         ret = -EINVAL;  
  91.         goto probe_free_mem_region;  
  92.     }  
  93.   
  94.     /*获取IRQ */  
  95.     host->irq = platform_get_irq(pdev, 0);  
  96.     if (host->irq == 0) {  
  97.         dev_err(&pdev->dev, "failed to get interrupt resource.\n");  
  98.         ret = -EINVAL;  
  99.         goto probe_iounmap;  
  100.     }  
  101.   
  102.     /* 注册IRQ*/  
  103.     if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {  
  104.         dev_err(&pdev->dev, "failed to request mci interrupt.\n");  
  105.         ret = -ENOENT;  
  106.         goto probe_iounmap;  
  107.     }  
  108.   
  109.     /* We get spurious interrupts even when we have set the IMSK 
  110.      * register to ignore everything, so use disable_irq() to make 
  111.      * ensure we don't lock the system with un-serviceable requests. */  
  112.   
  113.     /* 禁止中断并设置中断状态*/  
  114.     disable_irq(host->irq);  
  115.     host->irq_state = false;  
  116.   
  117.     /* 使用detect功能,则申请gpio */  
  118.     if (!host->pdata->no_detect) {  
  119.         ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");  
  120.         if (ret) {  
  121.             dev_err(&pdev->dev, "failed to get detect gpio\n");  
  122.             goto probe_free_irq;  
  123.         }  
  124.         /* 根据gpio获取中断号*/  
  125.         host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);  
  126.   
  127.         /* 中断号有效则注册该中断*/  
  128.         if (host->irq_cd >= 0) {  
  129.             if (request_irq(host->irq_cd, s3cmci_irq_cd,  
  130.                     IRQF_TRIGGER_RISING |  
  131.                     IRQF_TRIGGER_FALLING,  
  132.                     DRIVER_NAME, host)) {  
  133.                 dev_err(&pdev->dev,  
  134.                     "can't get card detect irq.\n");  
  135.                 ret = -ENOENT;  
  136.                 goto probe_free_gpio_cd;  
  137.             }  
  138.         } else {  
  139.             dev_warn(&pdev->dev,  
  140.                  "host detect has no irq available\n");  
  141.             gpio_direction_input(host->pdata->gpio_detect);  
  142.         }  
  143.     } else  
  144.         host->irq_cd = -1;  
  145.   
  146.       
  147.     /* 使用wprotect功能,则申请gpio */  
  148.     if (!host->pdata->no_wprotect) {  
  149.         ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");  
  150.         if (ret) {  
  151.             dev_err(&pdev->dev, "failed to get writeprotect\n");  
  152.             goto probe_free_irq_cd;  
  153.         }  
  154.           
  155.         gpio_direction_input(host->pdata->gpio_wprotect);  
  156.     }  
  157.   
  158.     /* depending on the dma state, get a dma channel to use. */  
  159.   
  160.     /* 如果使用DMA则申请相应的物理DMA通道*/  
  161.     if (s3cmci_host_usedma(host)) {  
  162.         /* 返回值为通道号*/  
  163.         host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,  
  164.                         host);  
  165.         if (host->dma < 0) {  
  166.             /* DMA申请失败,尝试使用IO操作*/  
  167.             dev_err(&pdev->dev, "cannot get DMA channel.\n");  
  168.             if (!s3cmci_host_canpio()) {  
  169.                 ret = -EBUSY;  
  170.                 goto probe_free_gpio_wp;  
  171.             } else {  
  172.                 dev_warn(&pdev->dev, "falling back to PIO.\n");  
  173.                 host->dodma = 0; /* 表示不使用DMA?/ 
  174.             } 
  175.         } 
  176.     } 
  177.     /* 获取clk*/  
  178.     host->clk = clk_get(&pdev->dev, "sdi");  
  179.     if (IS_ERR(host->clk)) {  
  180.         dev_err(&pdev->dev, "failed to find clock source.\n");  
  181.         ret = PTR_ERR(host->clk);  
  182.         host->clk = NULL;  
  183.         goto probe_free_dma;  
  184.     }  
  185.   
  186.     /*使能clk*/  
  187.     ret = clk_enable(host->clk);  
  188.     if (ret) {  
  189.         dev_err(&pdev->dev, "failed to enable clock source.\n");  
  190.         goto clk_free;  
  191.     }  
  192.   
  193.     host->clk_rate = clk_get_rate(host->clk);  /* 保存时钟频率*/  
  194.   
  195.     /*开始初始化mmc当中的字段*/  
  196.     mmc->ops     = &s3cmci_ops;  /* 给出控制器operation函数集*/  
  197.     mmc->ocr_avail   = MMC_VDD_32_33 | MMC_VDD_33_34;  
  198. #ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ  
  199.     mmc->caps    = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;  
  200. #else  
  201.     mmc->caps    = MMC_CAP_4_BIT_DATA;  
  202. #endif  
  203.     /* 计算最大和最小的工作频率*/  
  204.     mmc->f_min   = host->clk_rate / (host->clk_div * 256);  
  205.     mmc->f_max   = host->clk_rate / host->clk_div;  
  206.   
  207.     if (host->pdata->ocr_avail)  
  208.         mmc->ocr_avail = host->pdata->ocr_avail;  
  209.   
  210.     mmc->max_blk_count   = 4095; /* 一个请求的最大block数*/  
  211.     mmc->max_blk_size    = 4095; /* block的最大容量*/  
  212.     mmc->max_req_size    = 4095 * 512;  /* 一个请求的最大字节数*/  
  213.     mmc->max_seg_size    = mmc->max_req_size;  
  214.   
  215.     mmc->max_segs        = 128;  
  216.   
  217.     dbg(host, dbg_debug,  
  218.         "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",  
  219.         (host->is2440?"2440":""),  
  220.         host->base, host->irq, host->irq_cd, host->dma);  
  221.   
  222.     ret = s3cmci_cpufreq_register(host);  
  223.     if (ret) {  
  224.         dev_err(&pdev->dev, "failed to register cpufreq\n");  
  225.         goto free_dmabuf;  
  226.     }  
  227.   
  228.     /* 注册该mmc 控制器到子系统中*/  
  229.     ret = mmc_add_host(mmc);  
  230.     if (ret) {  
  231.         dev_err(&pdev->dev, "failed to add mmc host.\n");  
  232.         goto free_cpufreq;  
  233.     }  
  234.   
  235.     s3cmci_debugfs_attach(host);  
  236.   
  237.     /* 设置驱动数据*/  
  238.     platform_set_drvdata(pdev, mmc);  
  239.     dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc),  
  240.          s3cmci_host_usedma(host) ? "dma" : "pio",  
  241.          mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");  
  242.   
  243.     return 0;  
  244.   
  245.  free_cpufreq:  
  246.     s3cmci_cpufreq_deregister(host);  
  247.   
  248.  free_dmabuf:  
  249.     clk_disable(host->clk);  
  250.   
  251.  clk_free:  
  252.     clk_put(host->clk);  
  253.   
  254.  probe_free_dma:  
  255.     if (s3cmci_host_usedma(host))  
  256.         s3c2410_dma_free(host->dma, &s3cmci_dma_client);  
  257.   
  258.  probe_free_gpio_wp:  
  259.     if (!host->pdata->no_wprotect)  
  260.         gpio_free(host->pdata->gpio_wprotect);  
  261.   
  262.  probe_free_gpio_cd:  
  263.     if (!host->pdata->no_detect)  
  264.         gpio_free(host->pdata->gpio_detect);  
  265.   
  266.  probe_free_irq_cd:  
  267.     if (host->irq_cd >= 0)  
  268.         free_irq(host->irq_cd, host);  
  269.   
  270.  probe_free_irq:  
  271.     free_irq(host->irq, host);  
  272.   
  273.  probe_iounmap:  
  274.     iounmap(host->base);  
  275.   
  276.  probe_free_mem_region:  
  277.     release_mem_region(host->mem->start, resource_size(host->mem));  
  278.   
  279.  probe_free_gpio:  
  280.     for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)  
  281.         gpio_free(i);  
  282.   
  283.  probe_free_host:  
  284.     mmc_free_host(mmc);  
  285.   
  286.  probe_out:  
  287.     return ret;  
  288. }  

  这个函数想当的长,差不多300行,我们来简要分析下:

第一步,该函数首先调用mmc_alloc_host分配了struct mmc_host和truct s3cmci_host和两个结构体的内存空间,其中前者包含后者,而后者有一个指针指向前者。

该函数的详细的实现如下:

[cpp] view plain copy print ?
  1. /** 
  2.  *  mmc_alloc_host - initialise the per-host structure. 
  3.  *  @extra: sizeof private data structure 
  4.  *  @dev: pointer to host device model structure 
  5.  * 
  6.  *  Initialise the per-host structure. 
  7.  */  
  8. struct mmc_host *mmc_alloc_host(int extra, struct device *dev)  
  9. {  
  10.     int err;  
  11.     struct mmc_host *host;  
  12.   
  13.     host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);  
  14.     if (!host)  
  15.         return NULL;  
  16.   
  17.     /* scanning will be enabled when we're ready */  
  18.     host->rescan_disable = 1;  
  19.     idr_preload(GFP_KERNEL);  
  20.     /* 获取一个idr,通过自旋锁进行互斥保护*/  
  21.     spin_lock(&mmc_host_lock);  
  22.     err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);  
  23.     if (err >= 0)  
  24.         host->index = err;  
  25.     spin_unlock(&mmc_host_lock);  
  26.     idr_preload_end();  
  27.     if (err < 0)  
  28.         goto free;  
  29.   
  30.     /* 设置设备名*/  
  31.     dev_set_name(&host->class_dev, "mmc%d", host->index);  
  32.   
  33.     host->parent = dev;  
  34.     host->class_dev.parent = dev; /* 设置父设备*/  
  35.     host->class_dev.class = &mmc_host_class;  /* 设置设备所属的类*/  
  36.     device_initialize(&host->class_dev);  
  37.   
  38.     mmc_host_clk_init(host);  
  39.   
  40.     mutex_init(&host->slot.lock);  
  41.     host->slot.cd_irq = -EINVAL;  
  42.   
  43.     spin_lock_init(&host->lock);  
  44.     init_waitqueue_head(&host->wq);  
  45.     INIT_DELAYED_WORK(&host->detect, mmc_rescan);   /* 创建延时工作队列*/  
  46. #ifdef CONFIG_PM  
  47.     host->pm_notify.notifier_call = mmc_pm_notify; /* 通知链回调函数*/  
  48. #endif  
  49.   
  50.     /* 
  51.      * By default, hosts do not support SGIO or large requests. 
  52.      * They have to set these according to their abilities. 
  53.      */  
  54.     host->max_segs = 1;  
  55.     host->max_seg_size = PAGE_CACHE_SIZE;  
  56.   
  57.     host->max_req_size = PAGE_CACHE_SIZE;  
  58.     host->max_blk_size = 512;  
  59.     host->max_blk_count = PAGE_CACHE_SIZE / 512;  
  60.   
  61.     return host;  
  62.   
  63. free:  
  64.     kfree(host);  
  65.     return NULL;  
  66. }  
  67.   
  68. EXPORT_SYMBOL(mmc_alloc_host);  
   其大致过程如下:分配相应的结构体,设置标志位rescan_disable为1,表示禁止扫描SD卡。

                         随后利用idr分配了一个编号给该MMC控制器,并初始化了MMC控制器设备对象(device),

                         初始化了控制器时钟信息。

                         很重要的一步,初始化了一个工作(host->detect)为mmc_rescan,该函数将负责执行对SD卡的扫描,该函数将在后面描述。

                         最后,初始化了MMC控制器的访问块大小的能力。

第二步,根据控制器所使用的引脚,向gpio子系统申请该引脚。

第三步,获取板级设备信息(pdev->dev.platform_data),并初始化struct s3cmci_host中的一些字段。

第四步,控制器驱动决定时候需要使用DMA进行传输(host->dodma)。

第五步,和其他驱动一样,获得寄存器空间,申请IO内存,并完成映射工作。

第六步,和其他驱动一样,获取IRQ号,并注册该irq,中断服务程序(ISR)为s3cmci_irq函数,并且关闭中断。

第七步,根据板级设备信息,判断是否使用探测(detect)功能,如果使用则向gpio子系统申请该引脚,并为该gpio注册中断服务程序。

              中断服务程序为s3cmci_irq_cd函数,上下沿有效,该函数就是用来判断SD卡是否插入卡槽中的。。

第八步,根据板级设备信息,判断是否使用写保护(no_wprotect)功能,如果使用则向gpio子系统申请该引脚。

第九步,如果使用DMA传输,则对DMA进行相关的初始化工作。

第十步,获取clk,并使能,然后获取时钟速率。

第十一步,设置控制器的访问函数集为s3cmci_ops,该结构体包括了MMC控制器行为的抽象,非常重要,我们来看下:

[cpp] view plain copy print ?
  1. static struct mmc_host_ops s3cmci_ops = {  
  2.     .request    = s3cmci_request,  
  3.     .set_ios    = s3cmci_set_ios,  
  4.     .get_ro     = s3cmci_get_ro,        /* 判断是否只读*/  
  5.     .get_cd     = s3cmci_card_present,    /* 判断card是否存在*/  
  6.     .enable_sdio_irq = s3cmci_enable_sdio_irq,  
  7. };  
struct mmc_host_ops其中包含了很多MMC控制器行为的抽象,S3C2440的MMC控制器驱动只使用了5个,功能如下:

    第一个是请求函数,用于向SD卡发送命令,并获取应答。

    第二个用于设置MMC控制器参数。

    第三个用于判断SD卡是否为只读。

    第四个用于判断SD卡是否在卡槽内。

    第五个用户使能sdio中断。

接下来开始初始化struct mmc_host 结构体里的字段,这些字段的具体含义就不细说了。

第十二步,调用s3cmci_cpufreq_register注册一个通知链,有关通知链的东西就不在这里赘述了。

第十三步,调用mmc_add_host注册MMC控制器,该函数将在1.2小结叙说。

第十三步,调用s3cmci_debugfs_attach向debuf文件系统注册有关访问接口,debugfs有关的我们就略过了。

第十四步,保存mmc至驱动的私有平台数据中(dpev->dev->p->driver_data)。


作为MMC控制器的probe方法,自然而然的需要注册MMC控制器,从上面我们可以看到在进行大量的初始化工作后,最终在第十三步注册该控制器驱动。

下以小结我们来看看这个函数干了点什么。

1.2 函数mmc_add_host的使命

下列代码位于:linux/drivers/mmc/core/host.c

[cpp] view plain copy print ?
  1. /** 
  2.  *  mmc_add_host - initialise host hardware 
  3.  *  @host: mmc host 
  4.  * 
  5.  *  Register the host with the driver model. The host must be 
  6.  *  prepared to start servicing requests before this function 
  7.  *  completes. 
  8.  */  
  9. int mmc_add_host(struct mmc_host *host)  
  10. {  
  11.     int err;  
  12.   
  13.     WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&  
  14.         !host->ops->enable_sdio_irq);  
  15.   
  16.     /* 注册控制器设备实例*/  
  17.     err = device_add(&host->class_dev);  
  18.     if (err)  
  19.         return err;  
  20.   
  21.     /* 注册一个led trigger*/  
  22.     led_trigger_register_simple(dev_name(&host->class_dev), &host->led);  
  23.   
  24. #ifdef CONFIG_DEBUG_FS  
  25.     mmc_add_host_debugfs(host);  
  26. #endif  
  27.     mmc_host_clk_sysfs_init(host);  
  28.   
  29.   
  30.     mmc_start_host(host);  
  31.       
  32.     /* 注册一个通知链*/  
  33.     register_pm_notifier(&host->pm_notify);  
  34.   
  35.     return 0;  
  36. }  
  37.   
  38. EXPORT_SYMBOL(mmc_add_host);  

该函数首先调用device_add注册了一个设备实例,随后注册了一个led trigger。

并调用mmc_host_clk_sysfs_init,后者利用sysfs向用户提供了门控相关的信息,

接着调用mmc_start_host来启动MMC控制器的时钟,并且判断SD卡是否已在卡槽中。

最后,调用register_pm_notifier向注册了一个用于电源管理的通知链。

很明显,这里调用的5个函数,我们需要关心的是mmc_start_host函数。


[cpp] view plain copy print ?
  1. void mmc_start_host(struct mmc_host *host)  
  2. {  
  3.     host->f_init = max(freqs[0], host->f_min);  
  4.     host->rescan_disable = 0;  
  5.     /* 必须重新scan才能上电,则关闭mmc控制器*/  
  6.     if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)  
  7.         mmc_power_off(host);    /* 关闭MMC控制器*/  
  8.     else  
  9.         mmc_power_up(host); /* 打卡MMC控制器*/  
  10.     mmc_detect_change(host, 0);  
  11. }  
MMC_CAP2_NO_PRESCAN_POWERUP 表示需要在上电前扫面SD卡,

如果定义了该宏,则需要关闭MMC控制器的电压,否则打开电源,该宏默认是不定义的。

mmc_power_up函数还是比较复杂的,不过它不是我们关心的重点,简单说句,该函数最后会调用在1.1小结中MMC控制器的抽象行为函数中的

set_ios来使能MMC控制器的电源。

该函数最后调用mmc_detect_change来探测SD卡是否存在。

[cpp] view plain copy print ?
  1. /** 
  2.  *    mmc_detect_change - process change of state on a MMC socket 
  3.  *    @host: host which changed state. 
  4.  *    @delay: optional delay to wait before detection (jiffies) 
  5.  * 
  6.  *    MMC drivers should call this when they detect a card has been 
  7.  *    inserted or removed. The MMC layer will confirm that any 
  8.  *    present card is still functional, and initialize any newly 
  9.  *    inserted. 
  10.  */  
  11. void mmc_detect_change(struct mmc_host *host, unsigned long delay)  
  12. {  
  13. #ifdef CONFIG_MMC_DEBUG  
  14.     unsigned long flags;  
  15.     spin_lock_irqsave(&host->lock, flags);  
  16.     WARN_ON(host->removed);  
  17.     spin_unlock_irqrestore(&host->lock, flags);  
  18. #endif  
  19.     host->detect_change = 1;  
  20.     mmc_schedule_delayed_work(&host->detect, delay);  
  21. }  

最开始的宏中的代码可以直接略过了。
将标志位detect_change置1后,函数紧接着调用了mmc_schedule_delayed_work函数,该函数如下:
[cpp] view plain copy print ?
  1. /* 
  2.  * Internal function. Schedule delayed work in the MMC work queue. 
  3.  */  
  4. static int mmc_schedule_delayed_work(struct delayed_work *work,  
  5.                      unsigned long delay)  
  6. {  
  7.     return queue_delayed_work(workqueue, work, delay);  
  8. }  

该函数简单调用了queue_delayed_work来添加一个工作到工作队列workqueue中。

基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)中的第三章,MMC子系统在初始化时就注册了一个工作队列,并保存到全局变量workqueue中,

同时函数将detect工作添加到了该工作队列中,而detect工作的执行函数即为在1.1中分析时说道的mmc_rescan函数。

因此该函数的作用就是将detec表示的工作函数mmc_rescan添加到了工作队列中。

创建完工作队列后,mmc_start_host函数的使命也算结束了,它的存在极其短暂,但是它却调用了一个非常关键的函数mmc_detect_change,

后者创建的工作函数mmc_rescan将会扫描是否有SD卡插入。


2. SD卡扫描函数mmc_rescan

整个mmc_rescan函数执行分为两个部分。

第一部分,探测SD卡是否存在。

第二部分,按照SD规范,初始化SD卡。

2.1 detect SD卡

下列代码位于:linux/drivers/mmc/core/core.c

[cpp] view plain copy print ?
  1. void mmc_rescan(struct work_struct *work)  
  2. {  
  3.     struct mmc_host *host =  
  4.         container_of(work, struct mmc_host, detect.work);  
  5.     int i;  
  6.   
  7.     /* host禁止再次scan,则直接返回*/  
  8.     if (host->rescan_disable)  
  9.         return;  
  10.   
  11.     /* If there is a non-removable card registered, only scan once */  
  12.     /* 是不可插拔的sd卡,则只scan一次*/  
  13.     if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)  
  14.         return;  
  15.     host->rescan_entered = 1;  /* 表示至少scan一次了*/  
  16.   
  17.     mmc_bus_get(host);  /* 增加总线引用技术*/  
  18.   
  19.     /* 
  20.      * if there is a _removable_ card registered, check whether it is 
  21.      * still present 
  22.      */  
  23.      /* 如果总线提供detect方法则调用*/  
  24.     if (host->bus_ops && host->bus_ops->detect && !host->bus_dead  
  25.         && !(host->caps & MMC_CAP_NONREMOVABLE))  
  26.         host->bus_ops->detect(host);    
  27.   
  28.     host->detect_change = 0;  
  29.   
  30.     /* 
  31.      * Let mmc_bus_put() free the bus/bus_ops if we've found that 
  32.      * the card is no longer present. 
  33.      */  
  34.     mmc_bus_put(host);  /* 减少总线引用技术*/  
  35.     mmc_bus_get(host);  
  36.   
  37.     /* if there still is a card present, stop here */  
  38.     /* 有card存在,无需继续了*/  
  39.     if (host->bus_ops != NULL) {  
  40.         mmc_bus_put(host);  
  41.         goto out;  
  42.     }  
  43.   
  44.     /* 
  45.      * Only we can add a new handler, so it's safe to 
  46.      * release the lock here. 
  47.      */  
  48.     mmc_bus_put(host);  
  49.   
  50.     /* 调用get_cd方法,判断card是否存在*/  
  51.     if (host->ops->get_cd && host->ops->get_cd(host) == 0) {  
  52.         mmc_claim_host(host);  
  53.         mmc_power_off(host);  
  54.         mmc_release_host(host);  
  55.         goto out;  
  56.     }  
  57.   
  58.       
  59.     mmc_claim_host(host);  
  60.     /**/  
  61.     for (i = 0; i < ARRAY_SIZE(freqs); i++) {  
  62.         if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))  
  63.             break;  
  64.         if (freqs[i] <= host->f_min)  
  65.             break;  
  66.     }  
  67.     mmc_release_host(host);  
  68.   
  69.  out:  
  70.     /* 如果需要再次扫描*/  
  71.     if (host->caps & MMC_CAP_NEEDS_POLL)  
  72.         mmc_schedule_delayed_work(&host->detect, HZ);  
  73. }  

该函数首先判断rescan_disable是否为真,如果为真则不用扫描SD卡,直接推出了。 在mmc_start_host时,该变量被置为0,因此这里函数不返回,程序继续往下走。

你可能感兴趣的:(基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二))