上一篇文章介绍了SD卡的插入和拔出,下面就来看看SD卡的读写。SD卡在Linux中是块设备。该块设备驱动是在drivers/mmc/card/block.c文件中被定义的:
module_init(mmc_blk_init);
static int __init mmc_blk_init(void)
{
int res;
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
pr_info("mmcblk: using %dminors per device\n", perdev_minors);
max_devices = 256 / perdev_minors;
//注册MMC块设备
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
if (res)
goto out;
//注册MMC驱动
res =mmc_register_driver(&mmc_driver);
if (res)
goto out2;
return 0;
out2:
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
out:
return res;
}
全局静态结构mmc_driver定义为:
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
};
mmc_register_driver函数为:
intmmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type;
returndriver_register(&drv->drv);
}
从mmc_register_driver函数可以看出,MMC驱动总线类型为mmc_bus_type:
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,
.pm =&mmc_bus_pm_ops,
};
有了MMC驱动,那么MMC设备在哪里?它们又如何联系在一起呢?上一篇文章在介绍mmc_attach_sd函数的时候,调用了mmc_sd_init_card函数,在该函数内又调用了mmc_alloc_card函数用于分配SD卡结构。在mmc_alloc_card函数内,一条重要的语句是:
card->dev.bus = &mmc_bus_type;
也就是MMC设备的总线也是mmc_bus_type。然后又在mmc_attach_sd函数内调用了mmc_add_card函数,在该函数内通过调用device_add函数把mmc_card,也就是MMC设备添加到了mmc_bus_type总线类型上。无论是设备还是驱动,一旦注册成功就会通过.match的回调函数mmc_bus_match来寻找并匹配对方:
static int mmc_bus_match(struct device*dev, struct device_driver *drv)
{
return 1;
}
mmc_bus_match函数返回的总是1,说明只要添加到该总线上,该总线上的设备和驱动就永远匹配。匹配后调用.probe的回调函数mmc_bus_probe:
static int mmc_bus_probe(struct device*dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
return drv->probe(card);
}
在mmc_bus_probe函数内,通过drv->probe(card);调用了回调函数mmc_blk_probe:
static int mmc_blk_probe(struct mmc_card*card)
{
struct mmc_blk_data *md, *part_md;
char cap_str[10];
/*
* Check that the card supports the command class(es) we need.
*/
//检查SD卡是否支持块读命令
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
return -ENODEV;
//为SD卡分配mmc_blk_data结构空间
md = mmc_blk_alloc(card);
if (IS_ERR(md))
return PTR_ERR(md);
string_get_size((u64)get_capacity(md->disk) << 9,STRING_UNITS_2,
cap_str,sizeof(cap_str));
pr_info("%s: %s %s %s %s\n",
md->disk->disk_name,mmc_card_id(card), mmc_card_name(card),
cap_str, md->read_only ?"(ro)" : "");
//MMC的物理分区
if (mmc_blk_alloc_parts(card, md))
goto out;
//设置设备数据
mmc_set_drvdata(card, md);
mmc_fixup_device(card, blk_fixups);
//注册驱动
if (mmc_add_disk(md))
goto out;
list_for_each_entry(part_md,&md->part, part) {
if (mmc_add_disk(part_md))
goto out;
}
return 0;
out:
mmc_blk_remove_parts(card, md);
mmc_blk_remove_req(md);
return 0;
}
重要的mmc_blk_alloc函数:
static struct mmc_blk_data*mmc_blk_alloc(struct mmc_card *card)
{
sector_t size;
struct mmc_blk_data *md;
//根据卡的类型确定容量大小
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
/*
* The EXT_CSD sector countis in number or 512 byte
* sectors.
*/
size =card->ext_csd.sectors;
} else {
/*
* The CSD capacity field is inunits of read_blkbits.
* set_capacity takes units of512 bytes.
*/
size = card->csd.capacity<< (card->csd.read_blkbits - 9);
}
md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
MMC_BLK_DATA_AREA_MAIN);
return md;
}
static struct mmc_blk_data*mmc_blk_alloc_req(struct mmc_card *card,
struct device *parent,
sector_t size,
bool default_ro,
const char *subname,
int area_type)
{
struct mmc_blk_data *md;
int devidx, ret;
devidx = find_first_zero_bit(dev_use, max_devices);
if (devidx >= max_devices)
return ERR_PTR(-ENOSPC);
__set_bit(devidx, dev_use);
//分配结构体mmc_blk_data空间并初始化
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
if (!md) {
ret = -ENOMEM;
goto out;
}
/*
* !subname implies we are creating main mmc_blk_data that will be
* associated with mmc_card with mmc_set_drvdata. Due to device
* partitions, devidx will not coincide with a per-physical card
* index anymore so we keep track of a name index.
*/
if (!subname) {
md->name_idx =find_first_zero_bit(name_use, max_devices);
__set_bit(md->name_idx,name_use);
} else
md->name_idx = ((structmmc_blk_data *)
dev_to_disk(parent)->private_data)->name_idx;
md->area_type = area_type;
/*
* Set the read-only status based on the supported commands
* and the write protect switch.
*/
//设置SD卡是否为只读,即有没有写保护或是否支持块写功能
md->read_only = mmc_blk_readonly(card);
//分配设备的次设备号
md->disk = alloc_disk(perdev_minors);
if (md->disk == NULL) {
ret = -ENOMEM;
goto err_kfree;
}
spin_lock_init(&md->lock);
INIT_LIST_HEAD(&md->part);
md->usage = 1;
//初始化一个请求队列,并将该队列与SD卡关联
ret =mmc_init_queue(&md->queue, card, &md->lock, subname);
if (ret)
goto err_putdisk;
//注册mmc_blk_issue_rq到SD卡队列,当队列有请求待处理时,mmc_blk_issue_rq会被调用
md->queue.issue_fn = mmc_blk_issue_rq;
md->queue.data = md;
//为SD卡的gendisk赋值
md->disk->major = MMC_BLOCK_MAJOR;
md->disk->first_minor = devidx * perdev_minors;
md->disk->fops= &mmc_bdops;
md->disk->private_data = md;
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = parent;
//设置只读属性
set_disk_ro(md->disk, md->read_only || default_ro);
if (area_type & MMC_BLK_DATA_AREA_RPMB)
md->disk->flags |=GENHD_FL_NO_PART_SCAN;
/*
* As discussed on lkml, GENHD_FL_REMOVABLE should:
*
* - be set for removable media with permanent block devices
* - be unset for removable block devices with permanent media
*
* Since MMC block devices clearly fall under the second
* case, we do not set GENHD_FL_REMOVABLE. Userspace
* should use the block device creation/destruction hotplug
* messages to tell when the card is present.
*/
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
"mmcblk%d%s",md->name_idx, subname ? subname : "");
if (mmc_card_mmc(card))
blk_queue_logical_block_size(md->queue.queue,
card->ext_csd.data_sector_size);
else
blk_queue_logical_block_size(md->queue.queue, 512);
//设置SD卡的容量
set_capacity(md->disk, size);
if (mmc_host_cmd23(card->host)) {
if (mmc_card_mmc(card) ||
(mmc_card_sd(card)&&
card->scr.cmds &SD_SCR_CMD23_SUPPORT))
md->flags |= MMC_BLK_CMD23;
}
if (mmc_card_mmc(card) &&
md->flags& MMC_BLK_CMD23 &&
((card->ext_csd.rel_param& EXT_CSD_WR_REL_PARAM_EN) ||
card->ext_csd.rel_sectors)) {
md->flags |= MMC_BLK_REL_WR;
blk_queue_flush(md->queue.queue,REQ_FLUSH | REQ_FUA);
}
return md;
err_putdisk:
put_disk(md->disk);
err_kfree:
kfree(md);
out:
return ERR_PTR(ret);
}
在初始化SD卡队列函数mmc_init_queue中,调用了关键的blk_init_queue函数。当系统要申请SD块设备请求时,会调用blk_init_queue函数的第一参数所指向的回调函数mmc_request_fn,该函数又通过wake_up_process函数唤醒一个线程,这个线程还是在mmc_init_queue函数中定义的:
mq->thread =kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",
host->index, subname ?subname : "");
在线程函数mmc_queue_thread中的do while死循环中,有一条关键的语句:
mq->issue_fn(mq, req);
该条语句实际上调用的是mmc_blk_issue_rq函数,因为在mmc_blk_alloc_req函数内对mq->issue_fn进行了赋值:
md->queue.issue_fn = mmc_blk_issue_rq;
因此,对于MMC子系统来说,当系统要对SD卡读写时,具体的处理函数为mmc_blk_issue_rq,即mmc_blk_issue_rq函数是MMC子系统的请求处理函数。下面就来介绍mmc_blk_issue_rq函数:
static int mmc_blk_issue_rq(structmmc_queue *mq, struct request *req)
{
int ret;
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
if (req && !mq->mqrq_prev->req)
/* claim host only for thefirst request */
mmc_claim_host(card->host); //声明mmc_host
//主要用来修改EXT_CSD寄存器
ret = mmc_blk_part_switch(card, md);
if (ret) {
if (req) {
blk_end_request_all(req, -EIO);
}
ret = 0;
goto out;
}
if (req && req->cmd_flags & REQ_DISCARD) { //申请的事件是删除扇区
/* complete ongoing asynctransfer before issuing discard */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq,NULL);
if (req->cmd_flags &REQ_SECURE &&
!(card->quirks &MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
ret =mmc_blk_issue_secdiscard_rq(mq, req);
else
ret =mmc_blk_issue_discard_rq(mq, req);
} else if (req && req->cmd_flags & REQ_FLUSH) { //申请的事件是缓存刷新
/* complete ongoing asynctransfer before issuing flush */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq,NULL);
ret = mmc_blk_issue_flush(mq,req);
} else { //其他事件的处理
ret = mmc_blk_issue_rw_rq(mq,req);
}
out:
if (!req)
/* release host only when thereare no more requests */
mmc_release_host(card->host);
return ret;
}
请求处理函数mmc_blk_issue_rq主要调用的是mmc_blk_issue_rw_rq函数,而mmc_blk_issue_rw_rq函数主要调用了mmc_start_req函数,mmc_start_req函数又调用了__mmc_start_req函数,__mmc_start_req函数最后调用了mmc_start_request函数:
static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_DEBUG
unsigned int i, sz;
struct scatterlist *sg;
#endif
if (mrq->sbc) {
pr_debug("<%s: startingCMD%u arg %08x flags %08x>\n",
mmc_hostname(host),mrq->sbc->opcode,
mrq->sbc->arg,mrq->sbc->flags);
}
pr_debug("%s:starting CMD%u arg %08x flags %08x\n",
mmc_hostname(host),mrq->cmd->opcode,
mrq->cmd->arg,mrq->cmd->flags);
if(mrq->data) {
pr_debug("%s: blksz %d blocks %d flags %08x "
"tsac %d ms nsac %d\n",
mmc_hostname(host), mrq->data->blksz,
mrq->data->blocks, mrq->data->flags,
mrq->data->timeout_ns / 1000000,
mrq->data->timeout_clks);
}
if (mrq->stop) {
pr_debug("%s: CMD%u arg %08x flags %08x\n",
mmc_hostname(host),mrq->stop->opcode,
mrq->stop->arg,mrq->stop->flags);
}
WARN_ON(!host->claimed);
//以下都是为请求结构体赋值
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
BUG_ON(mrq->data->blksz> host->max_blk_size);
BUG_ON(mrq->data->blocks> host->max_blk_count);
BUG_ON(mrq->data->blocks* mrq->data->blksz >
host->max_req_size);
#ifdef CONFIG_MMC_DEBUG
sz = 0;
for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i)
sz += sg->length;
BUG_ON(sz !=mrq->data->blocks * mrq->data->blksz);
#endif
mrq->cmd->data = mrq->data;
mrq->data->error = 0;
mrq->data->mrq= mrq;
if (mrq->stop) {
mrq->data->stop =mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq =mrq;
}
}
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
//调用s3c2440的SD卡控制器读写函数
host->ops->request(host, mrq);
}
前面已经介绍过了,mmc_host->ops->request的回调函数为s3cmci_request,这样就又回到了drivers/mmc/host/s3cmci.c文件中:
static void s3cmci_request(struct mmc_host*mmc, struct mmc_request *mrq)
{
struct s3cmci_host *host = mmc_priv(mmc);
host->status = "mmc request";
host->cmd_is_stop = 0;
host->mrq = mrq;
//判断SD卡是否准备好了
if (s3cmci_card_present(mmc) == 0) {
dbg(host, dbg_err, "%s: nomedium present\n", __func__);
host->mrq->cmd->error= -ENOMEDIUM;
mmc_request_done(mmc, mrq); //终止请求
} else //SD卡准备好了,则发送请求
s3cmci_send_request(mmc);
}
static void s3cmci_send_request(structmmc_host *mmc)
{
struct s3cmci_host *host = mmc_priv(mmc);
struct mmc_request *mrq = host->mrq;
//判断请求是命令还是数据
struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop :mrq->cmd;
host->ccnt++;
prepare_dbgmsg(host, cmd, host->cmd_is_stop);
/* Clear command, data and fifo status registers
Fifo clear only necessary on 2440, but doesn't hurt on 2410
*/
//清空命令、数据、FIFO状态寄存器
writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
//发送数据
if (cmd->data) {
//配置数据控制寄存器
int res =s3cmci_setup_data(host, cmd->data);
host->dcnt++;
if (res) { //出现异常
dbg(host, dbg_err, "setupdata error %d\n", res);
cmd->error = res;
cmd->data->error= res;
//终止此次请求
mmc_request_done(mmc,mrq);
return;
}
if (s3cmci_host_usedma(host)) //DMA传输
res =s3cmci_prepare_dma(host, cmd->data);
else //FIFO传输,在s3cmci_probe函数中已默认配置为此种方式
res =s3cmci_prepare_pio(host, cmd->data);
if (res) { //传输数据失败
dbg(host, dbg_err,"data prepare error %d\n", res);
cmd->error = res;
cmd->data->error= res;
mmc_request_done(mmc,mrq);
return;
}
}
/* Send command */
//发送命令
s3cmci_send_command(host, cmd);
/* Enable Interrupt */
//使能SD传输中断
s3cmci_enable_irq(host, true);
}
我们主要看FIFO数据传输方式:
static int s3cmci_prepare_pio(structs3cmci_host *host, struct mmc_data *data)
{
//判断是读还是写
int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
host->pio_sgptr= 0;
host->pio_bytes = 0;
host->pio_count= 0;
host->pio_active = rw ? XFER_WRITE : XFER_READ;
if (rw) { //写
do_pio_write(host);
//当Tx FIFO填满一半时,触发中断
enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
} else { //读
//当Rx FIFO填满一半或只剩下最后一个字节时,触发中断
enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF
| S3C2410_SDIIMSK_RXFIFOLAST);
}
return 0;
}
发送命令函数:
static void s3cmci_send_command(structs3cmci_host *host,
structmmc_command *cmd)
{
u32 ccon, imsk;
//写SDI中断掩码寄存器——SDIIntMsk
//CRC状态错误、或命令响应超时、或接受命令响应、或命令发出、或响应CRC校验失败时触发SDI中断
imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
S3C2410_SDIIMSK_RESPONSECRC;
enable_imask(host, imsk);
//设置complete_what值,判断请求所处为何种状态,中断需要
if (cmd->data)
//数据传输结束,并且响应已接受
host->complete_what =COMPLETION_XFERFINISH_RSPFIN;
else if (cmd->flags & MMC_RSP_PRESENT)
//响应已接受
host->complete_what =COMPLETION_RSPFIN;
else
//命令已发出
host->complete_what =COMPLETION_CMDSENT;
//写SDI命令参数寄存器——SDICmdArg
writel(cmd->arg, host->base + S3C2410_SDICMDARG);
//确定命令种类
ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
ccon |= S3C2410_SDICMDCON_SENDERHOST| S3C2410_SDICMDCON_CMDSTART;
if (cmd->flags & MMC_RSP_PRESENT)
ccon |= S3C2410_SDICMDCON_WAITRSP;
if (cmd->flags & MMC_RSP_136)
ccon |= S3C2410_SDICMDCON_LONGRSP;
//写SDI命令控制寄存器——SDICmdCon
writel(ccon, host->base + S3C2410_SDICMDCON);
}
无论是读、写数据,还是发送命令,都离不开SD传输中断函数——s3cmci_irq:
static irqreturn_t s3cmci_irq(int irq, void*dev_id)
{
struct s3cmci_host *host = dev_id;
struct mmc_command *cmd;
u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
u32 mci_cclear = 0, mci_dclear;
unsigned long iflags;
//读取SDI数据状态寄存器
mci_dsta = readl(host->base + S3C2410_SDIDSTA);
//读取中断掩码位
mci_imsk = readl(host->base + host->sdiimsk);
if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT){ //SDIO中断检测
if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {
mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
writel(mci_dclear,host->base + S3C2410_SDIDSTA);
mmc_signal_sdio_irq(host->mmc);
return IRQ_HANDLED;
}
}
//关中断并保持状态字
spin_lock_irqsave(&host->complete_lock, iflags);
mci_csta = readl(host->base + S3C2410_SDICMDSTAT); //读取命令状态
mci_dcnt = readl(host->base + S3C2410_SDIDCNT); //读取数据保留计数器
mci_fsta = readl(host->base + S3C2410_SDIFSTA); //读取FIFO状态
mci_dclear = 0;
if ((host->complete_what == COMPLETION_NONE) || //没有请求
(host->complete_what == COMPLETION_FINALIZE)) { //请求已完成
host->status = "nothingto complete";
clear_imask(host);
goto irq_out;
}
if (!host->mrq) { //无请求
host->status = "no active mrq";
clear_imask(host);
goto irq_out;
}
//获得当前发送命令是否完成
cmd = host->cmd_is_stop ? host->mrq->stop :host->mrq->cmd;
if (!cmd) { //发送命令已完成
host->status = "noactive cmd";
clear_imask(host);
goto irq_out;
}
if (!s3cmci_host_usedma(host)) { //数据传输没有使用DMA模式
if ((host->pio_active ==XFER_WRITE) && //FIFO模式写数据
(mci_fsta & S3C2410_SDIFSTA_TFDET)) { //发送FIFO可用
disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
//中断的下半部分,即pio_tasklet函数
tasklet_schedule(&host->pio_tasklet);
host->status ="pio tx";
}
if ((host->pio_active ==XFER_READ) && //FIFO模式读数据
(mci_fsta & S3C2410_SDIFSTA_RFDET)) { //接受FIFO可用
disable_imask(host,
S3C2410_SDIIMSK_RXFIFOHALF|
S3C2410_SDIIMSK_RXFIFOLAST);
tasklet_schedule(&host->pio_tasklet);
host->status ="pio rx";
}
}
if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT){ //命令超时
dbg(host, dbg_err,"CMDSTAT: error CMDTIMEOUT\n");
cmd->error = -ETIMEDOUT;
host->status = "error:command timeout";
goto fail_transfer;
}
if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT){ //命令发送结束
if (host->complete_what ==COMPLETION_CMDSENT) {
host->status ="ok: command sent";
goto close_transfer;
}
mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
}
if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL){ //命令CRC错误
if (cmd->flags &MMC_RSP_CRC) {
if(host->mrq->cmd->flags & MMC_RSP_136) {
dbg(host,dbg_irq,
"fixup: ignore CRC fail with long rsp\n");
} else {
/* note, weused to fail the transfer
* here, but itseems that this is just
* the hardwaregetting it wrong.
*
*cmd->error = -EILSEQ;
*host->status = "error: bad command crc";
* gotofail_transfer;
*/
}
}
mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
}
if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN){ //收到命令响应
if (host->complete_what ==COMPLETION_RSPFIN) {
host->status ="ok: command response received";
goto close_transfer;
}
if (host->complete_what ==COMPLETION_XFERFINISH_RSPFIN)
host->complete_what= COMPLETION_XFERFINISH;
mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
}
/* errors handled after this point are only relevant
when a data transfer is in progress */
if (!cmd->data)
goto clear_status_bits;
/* Check for FIFO failure */
if (host->is2440) {
if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) { //FIFO错误
dbg(host, dbg_err, "FIFOfailure\n");
host->mrq->data->error =-EILSEQ;
host->status = "error: 2440 fifo failure";
goto fail_transfer;
}
} else {
if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
dbg(host, dbg_err,"FIFO failure\n");
cmd->data->error= -EILSEQ;
host->status ="error: fifo failure";
goto fail_transfer;
}
}
if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL){ //数据接受CRC错误
dbg(host, dbg_err, "baddata crc (outgoing)\n");
cmd->data->error =-EILSEQ;
host->status = "error: baddata crc (outgoing)";
goto fail_transfer;
}
if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL){ //发送数据后,CRC状态错误
dbg(host, dbg_err, "baddata crc (incoming)\n");
cmd->data->error =-EILSEQ;
host->status ="error: bad data crc (incoming)";
goto fail_transfer;
}
if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT){ //数据接受超时
dbg(host, dbg_err, "datatimeout\n");
cmd->data->error =-ETIMEDOUT;
host->status = "error:data timeout";
goto fail_transfer;
}
if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH){ //数据传输完成
if (host->complete_what ==COMPLETION_XFERFINISH) {
host->status ="ok: data transfer completed";
goto close_transfer;
}
if (host->complete_what ==COMPLETION_XFERFINISH_RSPFIN)
host->complete_what= COMPLETION_RSPFIN;
mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
}
//清除状态字
clear_status_bits:
writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
writel(mci_dclear, host->base + S3C2410_SDIDSTA);
goto irq_out;
//传输失败
fail_transfer:
host->pio_active = XFER_NONE;
//传输结束
close_transfer:
host->complete_what = COMPLETION_FINALIZE;
clear_imask(host);
tasklet_schedule(&host->pio_tasklet);
goto irq_out;
//中断退出
irq_out:
dbg(host, dbg_irq,
"csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08xstatus:%s.\n",
mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status);
//开中断并恢复状态字
spin_unlock_irqrestore(&host->complete_lock, iflags);
return IRQ_HANDLED;
}
在读、写数据时,中断处理还需要下半部分内容:
static void pio_tasklet(unsigned long data)
{
struct s3cmci_host *host = (struct s3cmci_host *) data;
//屏蔽中断
s3cmci_disable_irq(host, true);
//写数据
if (host->pio_active == XFER_WRITE)
do_pio_write(host);
//读数据
if (host->pio_active == XFER_READ)
do_pio_read(host);
//请求状态为完成状态
if (host->complete_what == COMPLETION_FINALIZE) {
clear_imask(host); //清中断屏蔽
if (host->pio_active !=XFER_NONE) {
dbg(host, dbg_err,"unfinished %s "
"-pio_count:[%u] pio_bytes:[%u]\n",
(host->pio_active == XFER_READ) ? "read" :"write",
host->pio_count,host->pio_bytes);
if(host->mrq->data)
host->mrq->data->error = -EINVAL;
}
s3cmci_enable_irq(host, false);
finalize_request(host); //完成请求处理
} else
s3cmci_enable_irq(host, true);
}
最后我们分析读数据和写数据函数:
static void do_pio_read(struct s3cmci_host*host)
{
int res;
u32 fifo;
u32 *ptr;
u32 fifo_words;
void __iomem *from_ptr;
/* write real prescaler to host, it might be set slow to fix */
//写波特率预定标器寄存器
writel(host->prescaler, host->base + S3C2410_SDIPRE);
//SDI数据寄存器的虚拟地址
from_ptr = host->base + host->sdidata;
//检查FIFO中当前的数据个数
while ((fifo = fifo_count(host))) {
if (!host->pio_bytes) {
//获取缓存的长度和开始地址
res =get_data_buffer(host, &host->pio_bytes,
&host->pio_ptr);
if (res) {
host->pio_active = XFER_NONE;
host->complete_what = COMPLETION_FINALIZE;
dbg(host,dbg_pio, "pio_read(): "
"complete(no more data).\n");
return;
}
dbg(host, dbg_pio,
"pio_read():new target: [%i]@[%p]\n",
host->pio_bytes,host->pio_ptr);
}
dbg(host, dbg_pio,
"pio_read():fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
fifo, host->pio_bytes,
readl(host->base + S3C2410_SDIDCNT));
/* If we have reached the endof the block, we can
* read a word and get 1 to 3bytes. If we in the
* middle of the block, we haveto read full words,
* otherwise we will writegarbage, so round down to
* an even multiple of 4. */
if (fifo >=host->pio_bytes) //FIFO中当前的数据多于数据段长度
fifo =host->pio_bytes;
else
fifo -= fifo & 3;
host->pio_bytes -= fifo; //更新FIFO数据长度
host->pio_count += fifo;
fifo_words = fifo >> 2; //字节转换为字
ptr = host->pio_ptr;
while (fifo_words--) //读数据
*ptr++ =readl(from_ptr);
host->pio_ptr = ptr;
if (fifo & 3) { //FIFO中的数据为非字对齐时的处理
u32 n = fifo & 3;
u32 data =readl(from_ptr);
u8 *p = (u8 *)host->pio_ptr;
while (n--) {
*p++ = data;
data >>=8;
}
}
}
if (!host->pio_bytes) { //没有要读取的数据
res = get_data_buffer(host,&host->pio_bytes, &host->pio_ptr);
if (res) {
dbg(host, dbg_pio,
"pio_read():complete (no more buffers).\n");
host->pio_active =XFER_NONE;
host->complete_what =COMPLETION_FINALIZE;
return;
}
}
//再次设置屏蔽寄存器,以再次触发中断,直到没有数据为止
enable_imask(host,
S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
}
static void do_pio_write(struct s3cmci_host*host)
{
void __iomem *to_ptr;
int res;
u32 fifo;
u32 *ptr;
//SDI数据寄存器的虚拟地址
to_ptr = host->base + host->sdidata;
// fifo_free函数用于检查FIFO的剩余空间
while ((fifo = fifo_free(host)) > 3) {
if (!host->pio_bytes) {
//获取缓存的长度和开始地址
res =get_data_buffer(host, &host->pio_bytes,
&host->pio_ptr);
if (res) {
dbg(host,dbg_pio,
"pio_write(): complete (no more data).\n");
host->pio_active = XFER_NONE;
return;
}
dbg(host, dbg_pio,
"pio_write():new source: [%i]@[%p]\n",
host->pio_bytes,host->pio_ptr);
}
/* If we have reached the endof the block, we have to
* write exactly the remainingnumber of bytes. If we
* in the middle of the block,we have to write full
* words, so round down to aneven multiple of 4. */
if (fifo >=host->pio_bytes) //FIFO剩余空间比这次要写入的数据段更大
fifo =host->pio_bytes;
else
fifo -= fifo & 3;
host->pio_bytes -= fifo; //更新FIFO
host->pio_count += fifo;
fifo = (fifo + 3) >> 2; //字节转换为字
ptr = host->pio_ptr;
while (fifo--) //写数据
writel(*ptr++, to_ptr);
host->pio_ptr = ptr;
}
//再次设置屏蔽寄存器
enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
}
关于DMA传输模式,在这里就不再介绍了。
下面我们就写一个简单的应用程序来测试一下SD卡的读写:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
char *buf1= "This is a SD card!";
char buf2[256];
int i =0, fd;
fd = open("/dev/mmcblk0p1", O_RDWR);
if (fd < 0){
printf("open file read error! ");
return -1;
}
i = write(fd,buf1,128);
if(i <0){
printf("error: write failed!");
return -1;
}
i=lseek(fd,0,SEEK_SET);
if(i<0){
printf("error: seek failed!");
return -1;
}
i=read(fd,buf2,128);
if (i < 0) {
printf("ERR: read failed! ");
return -1;
}
printf("%s\n",buf2);
return 0;
}
通过上面的测试程序,我们先向SD卡写入一串字符,然后再读出。如果写入的和读出的内容一致,则说明对SD卡的读写基本没有问题。