基于S3C2440的Linux-3.6.6移植——SD卡的应用(二),SD卡的读写

上一篇文章介绍了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卡的读写基本没有问题。

你可能感兴趣的:(基于S3C2440的Linux-3.6.6移植——SD卡的应用(二),SD卡的读写)