linux sd卡驱动分析

今天把驱动程序乱七八糟的看了一通,简单总结一下。
一个完整的驱动,需要提供如下的东西,
第一,用户空间/dev下面的设备节点。当然,如果该设备仅仅是内核的使用,例如I2C,则不需要在/dev下面建立设备节点。
第二,驱动程序,就是能到映射到/dev下面的fopen等系列操作。
中间有些负责,不过这些基本的东西都还是能够找到,具体细节上的联系,还需要后面认真分析。从简单的sd卡驱动来看这些内容都是可以找到的。
add_disk(md->disk)----》最终会向/dev下面添加节点。
md  =  mmc_blk_alloc(card); ----》会连接上实际的fopen等系列操作


-----------------------------------------------------华丽的分割线-------------------------------------------------------------------------
SD卡属于块设备,card驱动部分为了将SD卡驱动成为块设备。介绍该部分的内容时先介绍驱动结构体和接口函数结构体,然后介绍几个关键的驱动函数。

1.驱动的结构体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,  
    }; 

2.块设备操作结构体mmc_bdops

结构体mmc_bdops中定义了块设备操作的接口函数。

    static struct block_device_operations mmc_bdops = {  
        .open           = mmc_blk_open,  
        .release             = mmc_blk_release,  
        .getgeo         = mmc_blk_getgeo,  
        .owner          = THIS_MODULE,  
    }; 

3.块设备探针函数mmc_blk_probe()

该函数主要完成检验卡支持的命令,分配mmc_blk_data结构体空间,设置块的大小,最后设置card的driver_data 字段,并注册mmc信息到系统。

    static int mmc_blk_probe(struct mmc_card *card)  
    {  
        struct mmc_blk_data *md;  
        int err;  
        char cap_str[10];  
        /*检查卡支持的命令*/  
        if (!(card->csd.cmdclass & CCC_BLOCK_READ))  
            return -ENODEV;  
        /*为card分配mmc_blk_data结构体空间*/  
        md  =  mmc_blk_alloc(card);  
        /*设置块的大小*/  
        mmc_blk_set_blksize(md, card);  
        /*将md设置为card的driver_data 字段*/  
        mmc_set_drvdata(card, md);  
        /*把mmc包含的信息向系统进行注册,注册成功后就可以在文
    件系统对应目录下找到 mmc_card对应的结点设备*/  
        add_disk(md->disk);  
        return 0;  
    } 

4.驱动的入口函数mmc_blk_init()

加载驱动时该函数被调用,该函数向内核申请注册一个块设备,然后进入核心层进行注册。

    static int __init mmc_blk_init(void)  
    {  
        /*向内核申请注册一个块设备*/  
        res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");  
        /*进入核心层进行注册*/  
        mmc_register_driver(&mmc_driver);  
        return 0;  
    } 

5.为块设备分配空间函数mmc_blk_alloc()

函数mmc_blk_alloc()为块设备分配空间,并初始化一个请求队列,设置设备队列的sector大小。

    static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)  
    {  
        struct mmc_blk_data *md;  
        int devidx, ret;  
        /*在内存中查找第一个被清理过的bit*/  
        devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);  
        if (devidx >= MMC_NUM_MINORS)  
            return ERR_PTR(-ENOSPC);  
        /*从地址 dev_use开始设置bit,设置为devidx*/  
        __set_bit(devidx, dev_use);  
        /*分配结构体mmc_blk_data空间并初始化*/  
        md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);  
        /*设置卡的状态为只读*/  
        md->read_only = mmc_blk_readonly(card);  
        /*分配设备的次设备号为8*/  
        md->disk = alloc_disk(1 << MMC_SHIFT);  
        }  
        spin_lock_init(&md->lock);  
        md->usage = 1;  
        /*初始化一个请求队列,并将该队列与卡关联*/  
        mmc_init_queue(&md->queue, card, &md->lock);  
        /*注册 mmc_blk_issue_rq到md->queue,当md->queue上
    有request待处理时,    mmc_blk_issue_rq就会被调用*/  
        md->queue.issue_fn = mmc_blk_issue_rq;  
        md->queue.data = md;  
        /*注册相关的mmc_blk _data包含的块设备区*/  
        md->disk->major     = MMC_BLOCK_MAJOR;  
        md->disk->first_minor = devidx << MMC_SHIFT;  
        md->disk->fops = &mmc_bdops;  
        md->disk->private_data = md;  
        md->disk->queue = md->queue.queue;  
        md->disk->driverfs_dev = &card->dev;  
        sprintf(md->disk->disk_name, "mmcblk%d", devidx);  
        /*设置传输sector大小*/  
        blk_queue_hardsect_size(md->queue.queue, 512);  
        /*根据卡的类型设置容量*/  
        if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {  
            /*  
             * The EXT_CSD sector count is in number or 512 byte  
             * sectors.  
            */  
            set_capacity(md->disk, card->ext_csd.sectors);  
        } else {  
            /*  
             * The CSD capacity field is in units of read_blkbits.  
             * set_capacity takes units of 512 bytes.  
            */  
            set_capacity(md->disk,  
                card->csd.capacity << (card->csd.read_blkbits - 9));  
        }  
        return md;  
    } 

在该驱动部分还包括一些对块操作的函数,如mmc_blk_open()、mmc_blk_get()、mmc_blk_put()、mmc_blk_release()和mmc_blk_getgeo()


转于:http://edu.codepub.com/2010/0611/23407_2.php

mmc_set_ios(host) 中的关键语句 host->ops->set_ios(host, ios); 这里的 set_ios 实际上就是我们前面所提到的 .set_ios  = s3cmci_set_ios,

再看 mmc_detect_change(host, 0); 最后一句是

       mmc_schedule_delayed_work(&host->detect, delay);

实际上就是调用我们前面说的延时函数 mmc_rescan

mmc_power_up(host);// 这个函数实际上与前面的 mmc_power_off 类似,不过设置了启动时需要的 ios

                   mmc_go_idle(host);

                   //CMD0 , from inactive to idle

                   mmc_send_if_cond(host, host->ocr_avail);// 发送 SD_SEND_IF_COND ,是使用 SD2.0 卡才需要设置的命令

/*suppot for 2.0 card*/

                     * ...then normal SD...

                     */

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

                   if (!err) {

                            if (mmc_attach_sd(host, ocr))

                                     mmc_power_off(host);

                            goto out;

                   }

蓝色部分是遵照 SD 卡协议的 SD 卡启动过程,包括了非激活模式、卡识别模式和数据传输模式三种模式共九种状态的转换,你需要参照相关规范来理解。可以先参考下面三章图对模式和状态,以及状态转换有个初步了解。

 我们最初的 SD 卡的状态时 inactive 状态调用 mmc_go_idle(host) 后,发送命令 CMD0 是其处于 IDLE 状态。

我们详细分析一下 mmc_go_idle

memset(&cmd, 0, sizeof(struct mmc_command));

         cmd.opcode = MMC_GO_IDLE_STATE; MMC_GO_IDLE_STATE 就是命令 CMD0

         cmd.arg = 0; 此命令无参数

         cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;

         err = mmc_wait_for_cmd(host, &cmd, 0);// 见注 1

         mmc_delay(1);

注 1 : mmc_wait_for_cmd(host, &cmd, 0) 是用来发送命令的,我们揭开它的神秘面纱吧。

memset(&mrq, 0, sizeof(struct mmc_request));

         memset(cmd->resp, 0, sizeof(cmd->resp));

         cmd->retries = retries;

         mrq.cmd = cmd; 将命令嵌入到一个 mmc 请求中

         cmd->data = NULL;mmc 命令的 data 部分设置为 NULL, 这样表示我们要传输的是命令而不是数据

         mmc_wait_for_req(host, &mrq);// 关键部分

在该函数中调用了mmc_start_request ,而这个函数调用了host->ops->request(host, mrq) ,这个request 函数就是我们在前面分析的s3cmci_request ,这样MMC 核心第二次核HOST 层握手了

 我们再看看:        err = mmc_send_app_op_cond(host, 0, &ocr);// 注一

                   if (!err) {

                            if (mmc_attach_sd(host, ocr))// 注二

                                     mmc_power_off(host);

                            goto out;

注一:实际上是要发送 ACMD41 命令,这条命令可以用来获取 SDcard 的允许电压范围值,由于这是一条应用命令,所有发送它之前需要发送 CMD_55 命令。执行完后 card 状态变为 READY 获取的电压范围保存在 ocr 中,再调用 mmc_attach_sd(host, ocr) 看这个电压范围是否满足主机的要求,不满足,则 power_off 主机。

注二: mmc_attach_sd 完成匹配,和初始化卡的功能

host->ocr = mmc_select_voltage(host, ocr); 看是否匹配,如果匹配则做下面初始化工作

mmc_sd_init_card(host, host->ocr, NULL); 我们分析该函数

( 1 ) mmc_all_send_cid ()这个函数发生 CMD2 ,获取卡的身份信息,进入到身份状态

(2)card = mmc_alloc_card(host, &sd_type); 分配一张 SD 类型的 card 结构

(3) 接着调用 mmc_send_relative_add, 获取卡的相对地址,注意一前卡和主机通信都采用默认地址,现在有了自己的地址了,进入到 stand_by 状态

( 4 )通过发送 SEND_CSD (CMD9) 获取 CSD 寄存器的信息,包括 block 长度,卡容量等信息

(5) mmc_select_card(card) 发送 CMD7, 选中目前 RADD 地址上的卡,任何时候总线上只有一张卡被选中,进入了传输状态 ,

( 6 )调用 mmc_app_send_scr 发送命令 ACMD51 获取 SRC 寄存器的内容,进入到 SENDING-DATA 状态

在函数中还将获得的各个卡寄存器的内容解码,并保存到 cmd 结构的相应成员中。

( 7 ) if (host->ops->get_ro(host) > 0 )

                                     mmc_card_set_readonly(card);

通过调用 get_ro(host) 函数,实际上就是 s3cmci_get_ro 函数了。 我们判断是否写保护,如果是的,将 card 状态设置为只读状态

最后再 mmc_attach_sd 里,我们将 card 结构添加进去

mmc_add_card(host->card);

dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); 这里我们以 host 名 +rca 地址来命名卡我们可以看到在 /sys/devices/platform/s3c2440-sdi/mmc_host:mmc0/ 下出现 mmc0 : 0002 的目录,这个 0002 就是 rca 地址

到这里我们分析完了 MMC 的核心层。

linux-2.6.2x的mmc驱动与linux-2.6.1x的mmc驱动的区别 
在linux-2.6.2x中,mmc驱动用到的block_device_operations结构已重新定义,请看:
linux-2.6.1x:

struct block_device_operations {
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
    int (*media_changed) (struct gendisk *);
    int (*revalidate_disk) (struct gendisk *);
    struct module *owner;
};

linux-2.6.2x

struct block_device_operations {
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
    long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned, unsigned long);
    int (*direct_access) (struct block_device *, sector_t, unsigned long *);
    int (*media_changed) (struct gendisk *);
    int (*revalidate_disk) (struct gendisk *);
    int (*getgeo)(struct block_device *, struct hd_geometry *);
    struct module *owner;
};

注意到新版本的block驱动接口结构增加了gntgeo成员,使调用者可以直接调用此函数获得设备的几何结构。
工作流程: 
mmc驱动主要文件包括
drivers/mmc/card/block.c
drivers/mmc/card/queue.c
drivers/mmc/core/core.c
drivers/mmc/core/host.c
drivers/mmc/core/
内核启动时,首先执行core/core.c的mmc_init,注册mmc、sd总线,以及一个host class设备。接着执行card/block.c中,申请一个块设备。

数据结构: 
mmc总线操作相关函数,由于mmc卡支持多种总数据线,如SPI、SDIO、8LineMMC,而不同的总线的操作控制方式不尽相同,所以通过此结构与相应的总线回调函数相关联。

//总线操作结构
struct mmc_bus_ops {
    void (*remove)(struct mmc_host *);
    void (*detect)(struct mmc_host *);
    int (*sysfs_add)(struct mmc_host *, struct mmc_card *card);
    void (*sysfs_remove)(struct mmc_host *, struct mmc_card *card);
    void (*suspend)(struct mmc_host *);
    void (*resume)(struct mmc_host *);
};
//  mmc卡的总线操作 core/mmc.c
static const struct mmc_bus_ops mmc_ops = {
    .remove = mmc_remove,
    .detect = mmc_detect,
    .sysfs_add = mmc_sysfs_add,
    .sysfs_remove = mmc_sysfs_remove,
    .suspend = mmc_suspend,
    .resume = mmc_resume,
};
// sd卡的总线操作 core/sd.c
static const struct mmc_bus_ops mmc_sd_ops = {
    .remove = mmc_sd_remove,
    .detect = mmc_sd_detect,
    .sysfs_add = mmc_sd_sysfs_add,
    .sysfs_remove = mmc_sd_sysfs_remove,
    .suspend = mmc_sd_suspend,
    .resume = mmc_sd_resume,
};
// sdio的总线操作 core/sdio.c
static const struct mmc_bus_ops mmc_sdio_ops = {
    .remove = mmc_sdio_remove,
    .detect = mmc_sdio_detect,
};

关于总线操作的函数: 
.detect,驱动程序经常需要调用此函数去检测mmc卡的状态,具体实现是发送CMD13命令,并读回响应,如果响应错误,则依次调用.remove、detach_bus来移除卡及释放总线。

总体架构: 
kernel启动时,先后执行mmc_init()及mmc_blk_init(),以对mmc设备及mmc块模块进行初始化。
然后在挂载mmc设备驱动时,执行驱动程序中的xx_mmc_probe(),检测host设备中挂载的sd设备。此时probe函数会创建一个host设备,然后开启一个延时任务mmc_rescan()。
驱动挂载成功后,mmc_rescan()函数被执行,然后对卡进行初始化(步骤后面详细讲述)。
假如扫描到总线上挂有有效的设备,就调用相对应的函数把设备装到系统中,mmc_attach_sdio()、mmc_attach_sd()、mmc_attach_mmc()这三个函数分别是装载sdio设备,sd卡和mmc卡的。
在 sd卡中,驱动循环发送ACMD41、CMD55给卡,读取OCR寄存器,成功后,依次发送CMD2(读CID)、CMD3(得到RCA)、CMD9(读 CSD)、CMD7(选择卡)。后面还有几个命令分别是ACMD41&CMD51,使用CMD6切换一些功能,如切换到高速模式。
经过上述步骤,已经确定当前插入的卡是一张有效、可识别的存储卡。然后调用mmc_add_card()把存储卡加到系统中。正式与系统驱动连接在一起。
卡设备加到系统中后,通知mmc块设备驱动。块设备驱动此时调用probe函数,即mmc_blk_probe()函数,mmc_blk_probe()首先分配一个新的mmc_blk_data结构变量,然后调用mmc_init_queue,初始化blk队列。然后建立一个线程 mmc_queue_thread()。

mmc_rescan:mmc_rescan()函数是在驱动装载的时候,由驱动xx_mmc_probe()调用 mmc_alloc_host()时启动的一个延时任务。 xx_mmc_probe()->mmc_alloc_host()->INIT_DELAYED_WORK(&host->detect, mmc_rescan);

core部分 
1、取得总线
2、检查总线操作结构指针bus_ops,如果为空,则重新利用各总线对端口进行扫描,检测顺序依次为:SDIO、Normal SD、MMC。当检测到相应的卡类型后,就使用mmc_attach_bus()把相对应的总线操作与host连接起来。

void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
    ...
    host->bus_ops = ops;
    ...
}

3、初始化卡接以下流程初始化:
a、发送CMD0使卡进入IDLE状态
b、发送CMD8,检查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出了先发送CMD8,如响应为无效命令,则卡为SD1.1,否则就是SD2.0(请参考SD2.0 Spec)。
c、发送CMD5读取OCR寄存器。
d、发送ACMD55、CMD41,使卡进入工作状态。MMC卡并不支持ACMD55、CMD41,如果这步通过了,则证明这张卡是SD卡。
e、如果d步骤错误,则发送CMD1判断卡是否为MMC。SD卡不支持CMD1,而MMC卡支持,这就是SD和MMC类型的判断依据。
f、如果ACMD41和CMD1都不能通过,那这张卡恐怕就是无效卡了,初始化失败。




SD 卡驱动分析...................................................................................................................................1 块请求处理(linux/driver/mmc/card).....................................................................................2 1.probe.................................................................................................................................................2 2.do_request......................................................................................................................................13 3.小结................................................................................................................................................24 core 层处理(linux/driver/mmc/core)...................................................................................25 1.core 层初始化................................................................................................................................25 2.mmc_claim_host............................................................................................................................28 3.mmc_release_host..........................................................................................................................31 4.mmc_wait_for_req.........................................................................................................................33 5.mmc_wait_for_cmd.......................................................................................................................35 SD 控制器之初始化(linux/driver/mmc/host).....................................................................37 core 层续集之 mmc_add_host..................................................................................................49 SD 控制器之真正的硬件操作.................................................................................................82 1.s3cmci_get_ro................................................................................................................................83 2.s3cmci_set_ios...............................................................................................................................84 3.s3cmci_request 之命令处理..........................................................................................................86 4.s3cmci_request 之数据传输..........................................................................................................97 小结.........................................................................................................................................113
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值