注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

嵌入式开发实战4——内核的编译之十五MMC(SD、SDIO)卡的驱动说明2  

2012-07-16 20:50:34|  分类: ARM开发 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 

嵌入式开发实战4——内核的编译之十五MMC(SD、SDIO)卡的驱动说明2

 

 

公司的活儿比较忙,耽搁了两天,今天接着捋顺前面的驱动,从头开始,一点点的来,着急不得。先看一个整体上的通用流程:

 




2012年07月16日 - 还东国 - 还东国的博客
 


首先当然是要看Kconfig和makefile两个文件,MMC文件夹最外层的KCONFIG中:

if MMC

 

source "drivers/mmc/core/Kconfig"

 

source "drivers/mmc/card/Kconfig"

 

source "drivers/mmc/host/Kconfig"

 

endif # MM

所以要看具体的内容得去到具体的目录下看,先进入最早打交道的CARD文件夹下:

Kconfig: 

# MMC/SD card drivers

#

 

comment "MMC/SD/SDIO Card Drivers"

 

config MMC_BLOCK

    tristate "MMC block device driver"

    depends on BLOCK

    default y

    help

      Say Y here to enable the MMC block device driver support.

      This provides a block device driver, which you can use to

      mount the filesystem. Almost everyone wishing MMC support

      should say Y or M here.

 

config MMC_BLOCK_BOUNCE

    bool "Use bounce buffer for simple hosts"

    depends on MMC_BLOCK

    default y

    help

      SD/MMC is a high latency protocol where it is crucial to

      send large requests in order to get high performance. Many

      controllers, however, are restricted to continuous memory

      (i.e. they can't do scatter-gather), something the kernel

      rarely can provide.

 

      Say Y here to help these restricted hosts by bouncing

      requests back and forth from a large buffer. You will get

      a big performance gain at the cost of up to 64 KiB of

      physical memory.

 

      If unsure, say Y here.

后面的几个配置选项暂时先不理,所以也不贴上来,这里先说明一下什MMC_BLOCK_BOUNCE:

The Linux kernel configuration item
CONFIG_MMC_BLOCK_BOUNCE:


prompt: Use bounce buffer for simple hosts
type: bool
depends on:
CONFIG_MMC_BLOCK
defined in drivers/mmc/card/Kconfig
found in Linux kernels: 2.6.23–2.6.39, 3.0–3.4, 3.5-rc+HEAD

然后这里出现了一个bounce buffer这个概念:


bounce buffer在 IA-32 系统中,物理内存最开始的1GB 被称为“低端内存”,1GB 以上的部分称为“高端内存”。先前的Linux 核心版本要求通往存储设备的数据缓存必须放在物理RAM 的低端内存区域,即使是应用程序可以同时使用高端内存和低端内存也存在同样状况。这样,来自低端内存区域数据缓存的I/O 请求可以直接进行内存存取操作。但是,当应用程序发出一个I/O 请求,其中包含位于高端内存的数据缓存时,核心将强制在低端内存中分配一个临时数据缓存,并将位于高端内存的应用程序缓存数据复制到此处,这个数据缓存相当于一个跳转的buffer。例如一些老设备只能访问16M以下的内存,但DMA的目的地址却在16M以上时,就需要在设备能访问16M范围内设置一个buffer作为跳转。这种额外的数据拷贝被称为“bounce buffering”,会明显地降低I/O 密集的数据库应用的性能,因为大量分配的bounce buffers 会占用许多内存,而且bouncebuffer 的复制会增加系统内存总线的负荷。
所以说这个东西其实就是对以前版本的支持,没啥新东西。而在MAKEFILE里发现最主要的其实就两个文件:block.c 和queue.c,这个2.6.32.2和linux2.6.20还是有区别的,包括队列创建等都不一样。初始化也不太相同。但整体流程还是一致的,新版本只不过更清晰更易操作。
先看BLOCK这个文件,这个文件是CARD目录的入口,其初始化的动作很容易找到:

[card/block.c]

673 static int __init mmc_blk_init(void)       

674 {      

675     int res;   

676        

677     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); 

678     if (res)   

679         goto out;

680        

681     res = mmc_register_driver(&mmc_driver);

682     if (res)   

683         goto out2;

684        

685     return 0;  

686 out2: 

687     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");

688 out:  

689     return res;


690 }
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");  这个函数没有什么了不起的,只不过是弄个注册号,不清楚的可以看LDD3或者在网上搜。
res = mmc_register_driver(&mmc_driver); 这个是函数是在CORE里注册一下,怎么个注册法,看core/BUS.c:

164 int mmc_register_driver(struct mmc_driver *drv)

165 {  

166     drv->drv.bus = &mmc_bus_type;

167     return driver_register(&drv->drv);

168 }


很简单的两行,但却非常的麻烦,第一行是用来维护总线,第二行是注册设备驱动,一下子搞定驱动,总线,设备中的两大高手,不能不说麻烦事开始了。那个设备在哪儿呢,也就是driver,bus,device中的最后一个呢?别急,慢慢来。
先看总线,core/bus.c
 

139 static struct bus_type mmc_bus_type = {        

140     .name       = "mmc",

141     .dev_attrs  = mmc_dev_attrs,   

142     .match      = mmc_bus_match,

143     .uevent     = mmc_bus_uevent,

144     .probe      = mmc_bus_probe,

145     .remove     = mmc_bus_remove,

146     .suspend    = mmc_bus_suspend, 

147     .resume     = mmc_bus_resume,

148 };


在probe前要进行match匹配下,跳到这个函数:
/*
 * This currently matches any MMC driver to any MMC card - drivers
 * themselves make the decision whether to drive this card in their
 * probe method.
 */
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
    return 1;
}
 
相当酷,直接返回1,没有办法。那只好看probe的函数mmc_bus_probe:
仍然是core/bus.c:

99  static int mmc_bus_probe(struct device *dev)           

100 {          

101     struct mmc_driver *drv = to_mmc_driver(dev->driver);       

102     struct mmc_card *card = dev_to_mmc_card(dev);      

103            

104     return drv->probe(card);       


105 }
仍然很简单,但千万不要小看他们。
第一行和第二行,只不过是得到驱动和设备的驱动模型,第三行是调用驱动的probe函数,这样很简单,又回了到card/block.c:
 

663 static struct mmc_driver mmc_driver = {        

664     .drv        = {

665         .name   = "mmcblk",

666     },     

667     .probe      = mmc_blk_probe,

668     .remove     = mmc_blk_remove,

669     .suspend    = mmc_blk_suspend, 

670     .resume     = mmc_blk_resume,

671 };


那就没啥可说的,直奔正题:card/block.c

584 static int mmc_blk_probe(struct mmc_card *card)        

585 {          

586     struct mmc_blk_data *md;       

587     int err;       

588            

589     char cap_str[10];      

590            

591     /*     

592     * Check that the card supports the command class(es) we need.     

593     */    

594     if (!(card->csd.cmdclass & CCC_BLOCK_READ))    

595         return -ENODEV;

596            

597     md = mmc_blk_alloc(card);      

598     if (IS_ERR(md))    

599         return PTR_ERR(md);

600            

601     err = mmc_blk_set_blksize(md, card);       

602     if (err)       

603         goto out;  

604            

605     string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,      

606             cap_str, sizeof(cap_str));

607     printk(KERN_INFO "%s: %s %s %s %s\n",      

608         md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),   

609         cap_str, md->read_only ? "(ro)" : ""); 

610            

611     mmc_set_drvdata(card, md);     

612     add_disk(md->disk);    

613     return 0;      

614            

615 out:          

616     mmc_blk_put(md);       

617            

618     return err;    


619 }
一开始,定义了一个struct mmc_blk_data *md;结构,跳转过去,
/*
 * There is one mmc_blk_data per slot.
 */
struct mmc_blk_data {
    spinlock_t  lock;
    struct gendisk  *disk;
    struct mmc_queue queue;
 
    unsigned int    usage;
    unsigned int    read_only;
};
第一行是定义了一个自旋锁,啥是自旋锁,你要真不知道,只有去查书籍了,这个太通用了。第二行定义了一个gendisk结构,用来描述磁盘,第三行不用说,是一个队列,第四行和五行是一些状态的设置。
这里重点看看队列,上面在MAKEFILE里大家看到了,真正厉害的就两个文件,BLOCK和QUEUE,所以遇到队列,就杀进去看看。card/queue.h

7   struct mmc_queue {             

8       struct mmc_card     *card; 

9       struct task_struct  *thread;       

10      struct semaphore    thread_sem;    

11      unsigned int        flags; 

12      struct request      *req;  

13      int         (*issue_fn)(struct mmc_queue *, struct request *);

14      void            *data;

15      struct request_queue    *queue;    

16      struct scatterlist  *sg;       

17      char            *bounce_buf;

18      struct scatterlist  *bounce_sg;    

19      unsigned int        bounce_sg_len; 

20  }; 


说明白了这些,接着回到mmc_driver的probe中,static int mmc_blk_probe(struct mmc_card *card)
第597行,开始为 md分配内存空间,跳进去:

 

[card/block.c]

472 static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)           

473 {          

474     struct mmc_blk_data *md;       

475     int devidx, ret;       

476            

477     devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);     

478     if (devidx >= MMC_NUM_MINORS)      

479         return ERR_PTR(-ENOSPC);   

480     __set_bit(devidx, dev_use);    

481            

482     md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);     

483     if (!md) {     

484         ret = -ENOMEM; 

485         goto out;  

486     }      

487            

488            

489     /*     

490     * Set the read-only status based on the supported commands    

491     * and the write protect switch.       

492     */    

493     md->read_only = mmc_blk_readonly(card);    

494            

495     md->disk = alloc_disk(1 << MMC_SHIFT);     

496     if (md->disk == NULL) {    

497         ret = -ENOMEM; 

498         goto err_kfree;

499     }      

500            

501     spin_lock_init(&md->lock);     

502     md->usage = 1;     

503            

504     ret = mmc_init_queue(&md->queue, card, &md->lock);     

505     if (ret)       

506         goto err_putdisk;  

507            

508     md->queue.issue_fn = mmc_blk_issue_rq;     

509     md->queue.data = md;       

510            

511     md->disk->major = MMC_BLOCK_MAJOR; 

512     md->disk->first_minor = devidx << MMC_SHIFT;       

513     md->disk->fops = &mmc_bdops;       

514     md->disk->private_data = md;       

515     md->disk->queue = md->queue.queue;     

516     md->disk->driverfs_dev = &card->dev;       

517            

518     /*     

519     * As discussed on lkml, GENHD_FL_REMOVABLE should:    

520     *     

521     * - be set for removable media with permanent block devices       

522     * - be unset for removable block devices with permanent media     

523     *     

524     * Since MMC block devices clearly fall under the second       

525     * case, we do not set GENHD_FL_REMOVABLE.  Userspace      

526     * should use the block device creation/destruction hotplug    

527     * messages to tell when the card is present.      

528     */    

529            

530     sprintf(md->disk->disk_name, "mmcblk%d", devidx);      

531            

532     blk_queue_logical_block_size(md->queue.queue, 512);    

533            

534     if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {      

535         /* 

536         * The EXT_CSD sector count is in number or 512 byte   

537         * sectors.

538         */

539         set_capacity(md->disk, card->ext_csd.sectors); 

540     } else {       

541         /* 

542         * The CSD capacity field is in units of read_blkbits. 

543         * set_capacity takes units of 512 bytes.  

544         */

545         set_capacity(md->disk, 

546             card->csd.capacity << (card->csd.read_blkbits - 9));

547     }      

548     return md;     

549            

550 err_putdisk:          

551     put_disk(md->disk);    

552 err_kfree:        

553     kfree(md);     

554 out:          

555     return ERR_PTR(ret);       


556 }          
这个函数有点儿长,但仔细看却没什么复杂的,今天先说到这儿,明天接着说,饭要一口口的吃,不能着急。
努力要从今日始,虽然耽搁了几天,但仍要努力追赶回来。
时不我待,失不再来。

  评论这张
 
阅读(1290)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017