基于S3C2440的Linux-3.6.6移植——SD卡的应用(一),SD卡的插拔检测
在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系统所支持的文件格式,也许就不会出现上述的错误。更多推荐
所有评论(0)